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 cmd = u'SELECT COUNT(1) FROM dem.lnk_identity2ext_id WHERE fk_origin = %(issuer)s AND external_id = %(val)s'
57 args = {'issuer': pk_issuer, 'val': value}
58 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
59 return rows[0][0]
60
61
63 args = {
64 'last': lastnames,
65 'dob': dob
66 }
67 where_parts = [
68 u"lastnames = %(last)s",
69 u"dem.date_trunc_utc('day', dob) = dem.date_trunc_utc('day', %(dob)s)"
70 ]
71 if firstnames is not None:
72 if firstnames.strip() != u'':
73
74 where_parts.append(u"firstnames ~* %(first)s")
75 args['first'] = u'\\m' + firstnames
76 cmd = u"""SELECT COUNT(1) FROM dem.v_basic_person WHERE %s""" % u' AND '.join(where_parts)
77 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
78 return rows[0][0]
79
80
81
83
89
90
91
93 return 'firstnames lastnames dob gender'.split()
94
97
99 """Generate generic queries.
100
101 - not locale dependant
102 - data -> firstnames, lastnames, dob, gender
103
104 shall we mogrify name parts ? probably not as external
105 sources should know what they do
106
107 finds by inactive name, too, but then shows
108 the corresponding active name ;-)
109
110 Returns list of matching identities (may be empty)
111 or None if it was told to create an identity but couldn't.
112 """
113 where_snippets = []
114 args = {}
115
116 where_snippets.append(u'firstnames = %(first)s')
117 args['first'] = self.firstnames
118
119 where_snippets.append(u'lastnames = %(last)s')
120 args['last'] = self.lastnames
121
122 if self.dob is not None:
123 where_snippets.append(u"dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %(dob)s)")
124 args['dob'] = self.dob.replace(hour = 23, minute = 59, second = 59)
125
126 if self.gender is not None:
127 where_snippets.append('gender = %(sex)s')
128 args['sex'] = self.gender
129
130 cmd = u"""
131 SELECT *, '%s' AS match_type
132 FROM dem.v_basic_person
133 WHERE
134 pk_identity IN (
135 SELECT pk_identity FROM dem.v_person_names WHERE %s
136 )
137 ORDER BY lastnames, firstnames, dob""" % (
138 _('external patient source (name, gender, date of birth)'),
139 ' AND '.join(where_snippets)
140 )
141
142 try:
143 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx=True)
144 except:
145 _log.error(u'cannot get candidate identities for dto "%s"' % self)
146 _log.exception('query %s' % cmd)
147 rows = []
148
149 if len(rows) == 0:
150 _log.debug('no candidate identity matches found')
151 if not can_create:
152 return []
153 ident = self.import_into_database()
154 if ident is None:
155 return None
156 identities = [ident]
157 else:
158 identities = [ cIdentity(row = {'pk_field': 'pk_identity', 'data': row, 'idx': idx}) for row in rows ]
159
160 return identities
161
163 """Imports self into the database."""
164
165 self.identity = create_identity (
166 firstnames = self.firstnames,
167 lastnames = self.lastnames,
168 gender = self.gender,
169 dob = self.dob
170 )
171
172 if self.identity is None:
173 return None
174
175 for ext_id in self.external_ids:
176 try:
177 self.identity.add_external_id (
178 type_name = ext_id['name'],
179 value = ext_id['value'],
180 issuer = ext_id['issuer'],
181 comment = ext_id['comment']
182 )
183 except StandardError:
184 _log.exception('cannot import <external ID> from external data source')
185 _log.log_stack_trace()
186
187 for comm in self.comm_channels:
188 try:
189 self.identity.link_comm_channel (
190 comm_medium = comm['channel'],
191 url = comm['url']
192 )
193 except StandardError:
194 _log.exception('cannot import <comm channel> from external data source')
195 _log.log_stack_trace()
196
197 for adr in self.addresses:
198 try:
199 self.identity.link_address (
200 number = adr['number'],
201 street = adr['street'],
202 postcode = adr['zip'],
203 urb = adr['urb'],
204 state = adr['region'],
205 country = adr['country']
206 )
207 except StandardError:
208 _log.exception('cannot import <address> from external data source')
209 _log.log_stack_trace()
210
211 return self.identity
212
215
217 value = value.strip()
218 if value == u'':
219 return
220 name = name.strip()
221 if name == u'':
222 raise ValueError(_('<name> cannot be empty'))
223 issuer = issuer.strip()
224 if issuer == u'':
225 raise ValueError(_('<issuer> cannot be empty'))
226 self.external_ids.append({'name': name, 'value': value, 'issuer': issuer, 'comment': comment})
227
229 url = url.strip()
230 if url == u'':
231 return
232 channel = channel.strip()
233 if channel == u'':
234 raise ValueError(_('<channel> cannot be empty'))
235 self.comm_channels.append({'channel': channel, 'url': url})
236
237 - def remember_address(self, number=None, street=None, urb=None, region=None, zip=None, country=None):
238 number = number.strip()
239 if number == u'':
240 raise ValueError(_('<number> cannot be empty'))
241 street = street.strip()
242 if street == u'':
243 raise ValueError(_('<street> cannot be empty'))
244 urb = urb.strip()
245 if urb == u'':
246 raise ValueError(_('<urb> cannot be empty'))
247 zip = zip.strip()
248 if zip == u'':
249 raise ValueError(_('<zip> cannot be empty'))
250 country = country.strip()
251 if country == u'':
252 raise ValueError(_('<country> cannot be empty'))
253 region = region.strip()
254 if region == u'':
255 region = u'??'
256 self.addresses.append ({
257 u'number': number,
258 u'street': street,
259 u'zip': zip,
260 u'urb': urb,
261 u'region': region,
262 u'country': country
263 })
264
265
266
268 return u'<%s @ %s: %s %s (%s) %s>' % (
269 self.__class__.__name__,
270 id(self),
271 self.firstnames,
272 self.lastnames,
273 self.gender,
274 self.dob
275 )
276
278 """Do some sanity checks on self.* access."""
279
280 if attr == 'gender':
281 glist, idx = get_gender_list()
282 for gender in glist:
283 if str(val) in [gender[0], gender[1], gender[2], gender[3]]:
284 val = gender[idx['tag']]
285 object.__setattr__(self, attr, val)
286 return
287 raise ValueError('invalid gender: [%s]' % val)
288
289 if attr == 'dob':
290 if val is not None:
291 if not isinstance(val, pyDT.datetime):
292 raise TypeError('invalid type for DOB (must be datetime.datetime): %s [%s]' % (type(val), val))
293 if val.tzinfo is None:
294 raise ValueError('datetime.datetime instance is lacking a time zone: [%s]' % val.isoformat())
295
296 object.__setattr__(self, attr, val)
297 return
298
300 return getattr(self, attr)
301
302 -class cPersonName(gmBusinessDBObject.cBusinessDBObject):
303 _cmd_fetch_payload = u"SELECT * FROM dem.v_person_names WHERE pk_name = %s"
304 _cmds_store_payload = [
305 u"""UPDATE dem.names SET
306 active = FALSE
307 WHERE
308 %(active_name)s IS TRUE -- act only when needed and only
309 AND
310 id_identity = %(pk_identity)s -- on names of this identity
311 AND
312 active IS TRUE -- which are active
313 AND
314 id != %(pk_name)s -- but NOT *this* name
315 """,
316 u"""update dem.names set
317 active = %(active_name)s,
318 preferred = %(preferred)s,
319 comment = %(comment)s
320 where
321 id = %(pk_name)s and
322 id_identity = %(pk_identity)s and -- belt and suspenders
323 xmin = %(xmin_name)s""",
324 u"""select xmin as xmin_name from dem.names where id = %(pk_name)s"""
325 ]
326 _updatable_fields = ['active_name', 'preferred', 'comment']
327
336
338 return '%(last)s, %(title)s %(first)s%(nick)s' % {
339 'last': self._payload[self._idx['lastnames']],
340 'title': gmTools.coalesce (
341 self._payload[self._idx['title']],
342 map_gender2salutation(self._payload[self._idx['gender']])
343 ),
344 'first': self._payload[self._idx['firstnames']],
345 'nick': gmTools.coalesce(self._payload[self._idx['preferred']], u'', u" '%s'", u'%s')
346 }
347
348 description = property(_get_description, lambda x:x)
349
350 -class cIdentity(gmBusinessDBObject.cBusinessDBObject):
351 _cmd_fetch_payload = u"SELECT * FROM dem.v_basic_person WHERE pk_identity = %s"
352 _cmds_store_payload = [
353 u"""UPDATE dem.identity SET
354 gender = %(gender)s,
355 dob = %(dob)s,
356 dob_is_estimated = %(dob_is_estimated)s,
357 tob = %(tob)s,
358 cob = gm.nullify_empty_string(%(cob)s),
359 title = gm.nullify_empty_string(%(title)s),
360 fk_marital_status = %(pk_marital_status)s,
361 karyotype = gm.nullify_empty_string(%(karyotype)s),
362 pupic = gm.nullify_empty_string(%(pupic)s),
363 deceased = %(deceased)s,
364 emergency_contact = gm.nullify_empty_string(%(emergency_contact)s),
365 fk_emergency_contact = %(pk_emergency_contact)s,
366 fk_primary_provider = %(pk_primary_provider)s,
367 comment = gm.nullify_empty_string(%(comment)s)
368 WHERE
369 pk = %(pk_identity)s and
370 xmin = %(xmin_identity)s
371 RETURNING
372 xmin AS xmin_identity"""
373 ]
374 _updatable_fields = [
375 "title",
376 "dob",
377 "tob",
378 "cob",
379 "gender",
380 "pk_marital_status",
381 "karyotype",
382 "pupic",
383 'deceased',
384 'emergency_contact',
385 'pk_emergency_contact',
386 'pk_primary_provider',
387 'comment',
388 'dob_is_estimated'
389 ]
390
392 return self._payload[self._idx['pk_identity']]
394 raise AttributeError('setting ID of identity is not allowed')
395 ID = property(_get_ID, _set_ID)
396
398
399 if attribute == 'dob':
400 if value is not None:
401
402 if isinstance(value, pyDT.datetime):
403 if value.tzinfo is None:
404 raise ValueError('datetime.datetime instance is lacking a time zone: [%s]' % dt.isoformat())
405 else:
406 raise TypeError, '[%s]: type [%s] (%s) invalid for attribute [dob], must be datetime.datetime or None' % (self.__class__.__name__, type(value), value)
407
408
409 if self._payload[self._idx['dob']] is not None:
410 old_dob = gmDateTime.pydt_strftime (
411 self._payload[self._idx['dob']],
412 format = '%Y %m %d %H %M %S',
413 accuracy = gmDateTime.acc_seconds
414 )
415 new_dob = gmDateTime.pydt_strftime (
416 value,
417 format = '%Y %m %d %H %M %S',
418 accuracy = gmDateTime.acc_seconds
419 )
420 if new_dob == old_dob:
421 return
422
423 gmBusinessDBObject.cBusinessDBObject.__setitem__(self, attribute, value)
424
427
429 cmd = u"""
430 SELECT EXISTS (
431 SELECT 1
432 FROM clin.v_emr_journal
433 WHERE
434 pk_patient = %(pat)s
435 AND
436 soap_cat IS NOT NULL
437 )"""
438 args = {'pat': self._payload[self._idx['pk_identity']]}
439 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
440 return rows[0][0]
441
443 raise AttributeError('setting is_patient status of identity is not allowed')
444
445 is_patient = property(_get_is_patient, _set_is_patient)
446
448 cmd = u"SELECT pk FROM dem.staff WHERE fk_identity = %(pk)s"
449 args = {'pk': self._payload[self._idx['pk_identity']]}
450 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
451 if len(rows) == 0:
452 return None
453 return rows[0][0]
454
455 staff_id = property(_get_staff_id, lambda x:x)
456
457
458
461
462 gender_symbol = property(_get_gender_symbol, lambda x:x)
463
466
467 gender_string = property(_get_gender_string, lambda x:x)
468
470 names = self.get_names(active_only = True)
471 if len(names) == 0:
472 _log.error('cannot retrieve active name for patient [%s]', self._payload[self._idx['pk_identity']])
473 return None
474 return names[0]
475
476 active_name = property(get_active_name, lambda x:x)
477
478 - def get_names(self, active_only=False, exclude_active=False):
479
480 args = {'pk_pat': self._payload[self._idx['pk_identity']]}
481 where_parts = [u'pk_identity = %(pk_pat)s']
482 if active_only:
483 where_parts.append(u'active_name is True')
484 if exclude_active:
485 where_parts.append(u'active_name is False')
486 cmd = u"""
487 SELECT *
488 FROM dem.v_person_names
489 WHERE %s
490 ORDER BY active_name DESC, lastnames, firstnames
491 """ % u' AND '.join(where_parts)
492 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
493
494 if len(rows) == 0:
495
496 return []
497
498 names = [ cPersonName(row = {'idx': idx, 'data': r, 'pk_field': 'pk_name'}) for r in rows ]
499 return names
500
502 return _(u'%(last)s,%(title)s %(first)s%(nick)s (%(sex)s)') % {
503 'last': self._payload[self._idx['lastnames']],
504 'title': gmTools.coalesce(self._payload[self._idx['title']], u'', u' %s'),
505 'first': self._payload[self._idx['firstnames']],
506 'nick': gmTools.coalesce(self._payload[self._idx['preferred']], u'', u" '%s'"),
507 'sex': self.gender_symbol
508 }
509
511 return _(u'%(last)s,%(title)s %(first)s%(nick)s') % {
512 'last': self._payload[self._idx['lastnames']],
513 'title': gmTools.coalesce(self._payload[self._idx['title']], u'', u' %s'),
514 'first': self._payload[self._idx['firstnames']],
515 'nick': gmTools.coalesce(self._payload[self._idx['preferred']], u'', u" '%s'")
516 }
517
518 - def add_name(self, firstnames, lastnames, active=True):
519 """Add a name.
520
521 @param firstnames The first names.
522 @param lastnames The last names.
523 @param active When True, the new name will become the active one (hence setting other names to inactive)
524 @type active A types.BooleanType instance
525 """
526 name = create_name(self.ID, firstnames, lastnames, active)
527 if active:
528 self.refetch_payload()
529 return name
530
532 cmd = u"delete from dem.names where id = %(name)s and id_identity = %(pat)s"
533 args = {'name': name['pk_name'], 'pat': self.ID}
534 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
535
536
537
538
540 """
541 Set the nickname. Setting the nickname only makes sense for the currently
542 active name.
543 @param nickname The preferred/nick/warrior name to set.
544 """
545 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': u"select dem.set_nickname(%s, %s)", 'args': [self.ID, nickname]}])
546 self.refetch_payload()
547 return True
548
559
560 tags = property(get_tags, lambda x:x)
561
563 args = {
564 u'tag': tag,
565 u'identity': self.ID
566 }
567
568
569 cmd = u"SELECT pk FROM dem.identity_tag WHERE fk_tag = %(tag)s AND fk_identity = %(identity)s"
570 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
571 if len(rows) > 0:
572 return gmDemographicRecord.cIdentityTag(aPK_obj = rows[0]['pk'])
573
574
575 cmd = u"""
576 INSERT INTO dem.identity_tag (
577 fk_tag,
578 fk_identity
579 ) VALUES (
580 %(tag)s,
581 %(identity)s
582 )
583 RETURNING pk
584 """
585 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
586 return gmDemographicRecord.cIdentityTag(aPK_obj = rows[0]['pk'])
587
589 cmd = u"DELETE FROM dem.identity_tag WHERE pk = %(pk)s"
590 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': tag}}])
591
592
593
594
595
596
597
598
599
600
601 - def add_external_id(self, type_name=None, value=None, issuer=None, comment=None, pk_type=None):
602 """Adds an external ID to the patient.
603
604 creates ID type if necessary
605 """
606
607
608 if pk_type is not None:
609 cmd = u"""
610 select * from dem.v_external_ids4identity where
611 pk_identity = %(pat)s and
612 pk_type = %(pk_type)s and
613 value = %(val)s"""
614 else:
615
616 if issuer is None:
617 cmd = u"""
618 select * from dem.v_external_ids4identity where
619 pk_identity = %(pat)s and
620 name = %(name)s and
621 value = %(val)s"""
622 else:
623 cmd = u"""
624 select * from dem.v_external_ids4identity where
625 pk_identity = %(pat)s and
626 name = %(name)s and
627 value = %(val)s and
628 issuer = %(issuer)s"""
629 args = {
630 'pat': self.ID,
631 'name': type_name,
632 'val': value,
633 'issuer': issuer,
634 'pk_type': pk_type
635 }
636 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
637
638
639 if len(rows) == 0:
640
641 args = {
642 'pat': self.ID,
643 'val': value,
644 'type_name': type_name,
645 'pk_type': pk_type,
646 'issuer': issuer,
647 'comment': comment
648 }
649
650 if pk_type is None:
651 cmd = u"""insert into dem.lnk_identity2ext_id (external_id, fk_origin, comment, id_identity) values (
652 %(val)s,
653 (select dem.add_external_id_type(%(type_name)s, %(issuer)s)),
654 %(comment)s,
655 %(pat)s
656 )"""
657 else:
658 cmd = u"""insert into dem.lnk_identity2ext_id (external_id, fk_origin, comment, id_identity) values (
659 %(val)s,
660 %(pk_type)s,
661 %(comment)s,
662 %(pat)s
663 )"""
664
665 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
666
667
668 else:
669 row = rows[0]
670 if comment is not None:
671
672 if gmTools.coalesce(row['comment'], '').find(comment.strip()) == -1:
673 comment = '%s%s' % (gmTools.coalesce(row['comment'], '', '%s // '), comment.strip)
674 cmd = u"update dem.lnk_identity2ext_id set comment = %(comment)s where id=%(pk)s"
675 args = {'comment': comment, 'pk': row['pk_id']}
676 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
677
678 - def update_external_id(self, pk_id=None, type=None, value=None, issuer=None, comment=None):
679 """Edits an existing external ID.
680
681 Creates ID type if necessary.
682 """
683 cmd = u"""
684 UPDATE dem.lnk_identity2ext_id SET
685 fk_origin = (SELECT dem.add_external_id_type(%(type)s, %(issuer)s)),
686 external_id = %(value)s,
687 comment = gm.nullify_empty_string(%(comment)s)
688 WHERE
689 id = %(pk)s
690 """
691 args = {'pk': pk_id, 'value': value, 'type': type, 'issuer': issuer, 'comment': comment}
692 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
693
695 where_parts = ['pk_identity = %(pat)s']
696 args = {'pat': self.ID}
697
698 if id_type is not None:
699 where_parts.append(u'name = %(name)s')
700 args['name'] = id_type.strip()
701
702 if issuer is not None:
703 where_parts.append(u'issuer = %(issuer)s')
704 args['issuer'] = issuer.strip()
705
706 cmd = u"SELECT * FROM dem.v_external_ids4identity WHERE %s" % ' AND '.join(where_parts)
707 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
708
709 return rows
710
711 external_ids = property(get_external_ids, lambda x:x)
712
714 cmd = u"""
715 delete from dem.lnk_identity2ext_id
716 where id_identity = %(pat)s and id = %(pk)s"""
717 args = {'pat': self.ID, 'pk': pk_ext_id}
718 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
719
721 """Merge another identity into this one.
722
723 Keep this one. Delete other one."""
724
725 if other_identity.ID == self.ID:
726 return True, None
727
728 curr_pat = gmCurrentPatient()
729 if curr_pat.connected:
730 if other_identity.ID == curr_pat.ID:
731 return False, _('Cannot merge active patient into another patient.')
732
733 queries = []
734 args = {'pat2del': other_identity.ID, 'pat2keep': self.ID}
735
736
737 queries.append ({
738 'cmd': u"""
739 UPDATE clin.allergy_state SET
740 has_allergy = greatest (
741 (SELECT has_allergy FROM clin.v_pat_allergy_state WHERE pk_patient = %(pat2del)s),
742 (SELECT has_allergy FROM clin.v_pat_allergy_state WHERE pk_patient = %(pat2keep)s)
743 )
744 WHERE
745 pk = (SELECT pk_allergy_state FROM clin.v_pat_allergy_state WHERE pk_patient = %(pat2keep)s)
746 """,
747 'args': args
748 })
749
750 queries.append ({
751 'cmd': u'delete from clin.allergy_state where pk = (select pk_allergy_state from clin.v_pat_allergy_state where pk_patient = %(pat2del)s)',
752 'args': args
753 })
754
755
756
757 queries.append ({
758 'cmd': u"""
759 UPDATE dem.names d_n1 SET
760 lastnames = lastnames || ' (%s)'
761 WHERE
762 d_n1.id_identity = %%(pat2del)s
763 AND
764 EXISTS (
765 SELECT 1 FROM dem.names d_n2
766 WHERE
767 d_n2.id_identity = %%(pat2keep)s
768 AND
769 d_n2.lastnames = d_n1.lastnames
770 AND
771 d_n2.firstnames = d_n1.firstnames
772 )""" % _('assimilated'),
773 'args': args
774 })
775
776 queries.append ({
777 'cmd': u"""
778 UPDATE dem.names SET
779 id_identity = %(pat2keep)s
780 WHERE id_identity = %(pat2del)s AND active IS false""",
781 'args': args
782 })
783
784 queries.append ({
785 'cmd': u"""
786 INSERT INTO dem.names (
787 id_identity, active, lastnames, firstnames, preferred, comment
788 ) SELECT
789 %(pat2keep)s, false, lastnames, firstnames, preferred, comment
790 FROM dem.names d_n
791 WHERE d_n.id_identity = %(pat2del)s AND d_n.active IS true""",
792 'args': args
793 })
794
795
796 FKs = gmPG2.get_foreign_keys2column (
797 schema = u'dem',
798 table = u'identity',
799 column = u'pk'
800 )
801
802
803
804 queries.append ({
805 'cmd': u"""
806 UPDATE dem.lnk_identity2comm
807 SET url = url || ' (%s %s)'
808 WHERE
809 fk_identity = %%(pat2del)s
810 AND
811 EXISTS (
812 SELECT 1 FROM dem.lnk_identity2comm d_li2c
813 WHERE d_li2c.fk_identity = %%(pat2keep)s AND d_li2c.url = url
814 )
815 """ % (_('merged'), gmDateTime.pydt_strftime()),
816 'args': args
817 })
818
819 queries.append ({
820 'cmd': u"""
821 UPDATE dem.lnk_identity2ext_id
822 SET external_id = external_id || ' (%s %s)'
823 WHERE
824 id_identity = %%(pat2del)s
825 AND
826 EXISTS (
827 SELECT 1 FROM dem.lnk_identity2ext_id d_li2e
828 WHERE
829 d_li2e.id_identity = %%(pat2keep)s
830 AND
831 d_li2e.external_id = external_id
832 AND
833 d_li2e.fk_origin = fk_origin
834 )
835 """ % (_('merged'), gmDateTime.pydt_strftime()),
836 'args': args
837 })
838
839 queries.append ({
840 'cmd': u"""
841 DELETE FROM dem.lnk_person_org_address
842 WHERE
843 id_identity = %(pat2del)s
844 AND
845 id_address IN (
846 SELECT id_address FROM dem.lnk_person_org_address d_lpoa
847 WHERE d_lpoa.id_identity = %(pat2keep)s
848 )
849 """,
850 'args': args
851 })
852
853
854 cmd_template = u'UPDATE %s SET %s = %%(pat2keep)s WHERE %s = %%(pat2del)s'
855 for FK in FKs:
856 if FK['referencing_table'] == u'dem.names':
857 continue
858 queries.append ({
859 'cmd': cmd_template % (FK['referencing_table'], FK['referencing_column'], FK['referencing_column']),
860 'args': args
861 })
862
863
864 queries.append ({
865 'cmd': u'delete from dem.identity where pk = %(pat2del)s',
866 'args': args
867 })
868
869 _log.warning('identity [%s] is about to assimilate identity [%s]', self.ID, other_identity.ID)
870
871 gmPG2.run_rw_queries(link_obj = link_obj, queries = queries, end_tx = True)
872
873 self.add_external_id (
874 type_name = u'merged GNUmed identity primary key',
875 value = u'GNUmed::pk::%s' % other_identity.ID,
876 issuer = u'GNUmed'
877 )
878
879 return True, None
880
881
883 cmd = u"""
884 insert into clin.waiting_list (fk_patient, urgency, comment, area, list_position)
885 values (
886 %(pat)s,
887 %(urg)s,
888 %(cmt)s,
889 %(area)s,
890 (select coalesce((max(list_position) + 1), 1) from clin.waiting_list)
891 )"""
892 args = {'pat': self.ID, 'urg': urgency, 'cmt': comment, 'area': zone}
893 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], verbose = True)
894
896 cmd = u"""SELECT * FROM clin.v_waiting_list WHERE pk_identity = %(pat)s"""
897 args = {'pat': self.ID}
898 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
899 return rows
900
901 waiting_list_entries = property(get_waiting_list_entry, lambda x:x)
902
906
907 export_tray = property(_get_export_tray, lambda x:x)
908
909 - def export_as_gdt(self, filename=None, encoding='iso-8859-15', external_id_type=None):
910
911 template = u'%s%s%s\r\n'
912
913 file = codecs.open (
914 filename = filename,
915 mode = 'wb',
916 encoding = encoding,
917 errors = 'strict'
918 )
919
920 file.write(template % (u'013', u'8000', u'6301'))
921 file.write(template % (u'013', u'9218', u'2.10'))
922 if external_id_type is None:
923 file.write(template % (u'%03d' % (9 + len(str(self.ID))), u'3000', self.ID))
924 else:
925 ext_ids = self.get_external_ids(id_type = external_id_type)
926 if len(ext_ids) > 0:
927 file.write(template % (u'%03d' % (9 + len(ext_ids[0]['value'])), u'3000', ext_ids[0]['value']))
928 file.write(template % (u'%03d' % (9 + len(self._payload[self._idx['lastnames']])), u'3101', self._payload[self._idx['lastnames']]))
929 file.write(template % (u'%03d' % (9 + len(self._payload[self._idx['firstnames']])), u'3102', self._payload[self._idx['firstnames']]))
930 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')))
931 file.write(template % (u'010', u'3110', gmXdtMappings.map_gender_gm2xdt[self._payload[self._idx['gender']]]))
932 file.write(template % (u'025', u'6330', 'GNUmed::9206::encoding'))
933 file.write(template % (u'%03d' % (9 + len(encoding)), u'6331', encoding))
934 if external_id_type is None:
935 file.write(template % (u'029', u'6332', u'GNUmed::3000::source'))
936 file.write(template % (u'017', u'6333', u'internal'))
937 else:
938 if len(ext_ids) > 0:
939 file.write(template % (u'029', u'6332', u'GNUmed::3000::source'))
940 file.write(template % (u'%03d' % (9 + len(external_id_type)), u'6333', external_id_type))
941
942 file.close()
943
944
945
948
950 """Link an occupation with a patient, creating the occupation if it does not exists.
951
952 @param occupation The name of the occupation to link the patient to.
953 """
954 if (activities is None) and (occupation is None):
955 return True
956
957 occupation = occupation.strip()
958 if len(occupation) == 0:
959 return True
960
961 if activities is not None:
962 activities = activities.strip()
963
964 args = {'act': activities, 'pat_id': self.pk_obj, 'job': occupation}
965
966 cmd = u"select activities from dem.v_person_jobs where pk_identity = %(pat_id)s and l10n_occupation = _(%(job)s)"
967 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
968
969 queries = []
970 if len(rows) == 0:
971 queries.append ({
972 'cmd': u"INSERT INTO dem.lnk_job2person (fk_identity, fk_occupation, activities) VALUES (%(pat_id)s, dem.create_occupation(%(job)s), %(act)s)",
973 'args': args
974 })
975 else:
976 if rows[0]['activities'] != activities:
977 queries.append ({
978 '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))",
979 'args': args
980 })
981
982 rows, idx = gmPG2.run_rw_queries(queries = queries)
983
984 return True
985
987 if occupation is None:
988 return True
989 occupation = occupation.strip()
990 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))"
991 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj, 'job': occupation}}])
992 return True
993
994
995
997 cmd = u"select * from dem.v_person_comms where pk_identity = %s"
998 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}], get_col_idx = True)
999
1000 filtered = rows
1001
1002 if comm_medium is not None:
1003 filtered = []
1004 for row in rows:
1005 if row['comm_type'] == comm_medium:
1006 filtered.append(row)
1007
1008 return [ gmDemographicRecord.cCommChannel(row = {
1009 'pk_field': 'pk_lnk_identity2comm',
1010 'data': r,
1011 'idx': idx
1012 }) for r in filtered
1013 ]
1014
1015 - def link_comm_channel(self, comm_medium=None, url=None, is_confidential=False, pk_channel_type=None):
1016 """Link a communication medium with a patient.
1017
1018 @param comm_medium The name of the communication medium.
1019 @param url The communication resource locator.
1020 @type url A types.StringType instance.
1021 @param is_confidential Wether the data must be treated as confidential.
1022 @type is_confidential A types.BooleanType instance.
1023 """
1024 comm_channel = gmDemographicRecord.create_comm_channel (
1025 comm_medium = comm_medium,
1026 url = url,
1027 is_confidential = is_confidential,
1028 pk_channel_type = pk_channel_type,
1029 pk_identity = self.pk_obj
1030 )
1031 return comm_channel
1032
1038
1039
1040
1042
1043 cmd = u"SELECT * FROM dem.v_pat_addresses WHERE pk_identity = %(pat)s"
1044 args = {'pat': self.pk_obj}
1045 if address_type is not None:
1046 cmd = cmd + u" AND address_type = %(typ)s"
1047 args['typ'] = address_type
1048
1049 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
1050
1051 return [
1052 gmDemographicRecord.cPatientAddress(row = {'idx': idx, 'data': r, 'pk_field': 'pk_address'})
1053 for r in rows
1054 ]
1055
1056 - 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):
1057 """Link an address with a patient, creating the address if it does not exists.
1058
1059 @param id_type The primary key of the address type.
1060 """
1061 if address is None:
1062 address = gmDemographicRecord.create_address (
1063 country = country,
1064 state = state,
1065 urb = urb,
1066 suburb = suburb,
1067 postcode = postcode,
1068 street = street,
1069 number = number,
1070 subunit = subunit
1071 )
1072
1073 if address is None:
1074 return None
1075
1076
1077 cmd = u"SELECT id_address FROM dem.lnk_person_org_address WHERE id_identity = %(pat)s AND id_address = %(adr)s"
1078 args = {'pat': self.pk_obj, 'adr': address['pk_address']}
1079 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
1080
1081
1082 if len(rows) == 0:
1083 args = {'id': self.pk_obj, 'adr': address['pk_address'], 'type': id_type}
1084 cmd = u"""
1085 INSERT INTO dem.lnk_person_org_address(id_identity, id_address)
1086 VALUES (%(id)s, %(adr)s)
1087 RETURNING id_address"""
1088 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
1089
1090 linked_adr = gmDemographicRecord.cPatientAddress(aPK_obj = rows[0]['id_address'])
1091
1092
1093 if id_type is not None:
1094 linked_adr['pk_address_type'] = id_type
1095 linked_adr.save()
1096
1097 return linked_adr
1098
1100 """Remove an address from the patient.
1101
1102 The address itself stays in the database.
1103 The address can be either cAdress or cPatientAdress.
1104 """
1105 if pk_address is None:
1106 args = {'person': self.pk_obj, 'adr': address['pk_address']}
1107 else:
1108 args = {'person': self.pk_obj, 'adr': pk_address}
1109 cmd = u"DELETE FROM dem.lnk_person_org_address WHERE id_identity = %(person)s AND id_address = %(adr)s"
1110 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1111
1112
1113
1115 cmd = u"""
1116 select
1117 t.description,
1118 vbp.pk_identity as id,
1119 title,
1120 firstnames,
1121 lastnames,
1122 dob,
1123 cob,
1124 gender,
1125 karyotype,
1126 pupic,
1127 pk_marital_status,
1128 marital_status,
1129 xmin_identity,
1130 preferred
1131 from
1132 dem.v_basic_person vbp, dem.relation_types t, dem.lnk_person2relative l
1133 where
1134 (
1135 l.id_identity = %(pk)s and
1136 vbp.pk_identity = l.id_relative and
1137 t.id = l.id_relation_type
1138 ) or (
1139 l.id_relative = %(pk)s and
1140 vbp.pk_identity = l.id_identity and
1141 t.inverse = l.id_relation_type
1142 )"""
1143 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj}}])
1144 if len(rows) == 0:
1145 return []
1146 return [(row[0], cIdentity(row = {'data': row[1:], 'idx':idx, 'pk_field': 'pk'})) for row in rows]
1147
1149
1150 id_new_relative = create_dummy_identity()
1151
1152 relative = cIdentity(aPK_obj=id_new_relative)
1153
1154
1155 relative.add_name( '**?**', self.get_names()['lastnames'])
1156
1157 if self._ext_cache.has_key('relatives'):
1158 del self._ext_cache['relatives']
1159 cmd = u"""
1160 insert into dem.lnk_person2relative (
1161 id_identity, id_relative, id_relation_type
1162 ) values (
1163 %s, %s, (select id from dem.relation_types where description = %s)
1164 )"""
1165 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': [self.ID, id_new_relative, rel_type ]}])
1166 return True
1167
1169
1170 self.set_relative(None, relation)
1171
1176
1177 emergency_contact_in_database = property(_get_emergency_contact_from_database, lambda x:x)
1178
1179
1180
1189
1230
1231 - def dob_in_range(self, min_distance=u'1 week', max_distance=u'1 week'):
1232 cmd = u'select dem.dob_is_in_range(%(dob)s, %(min)s, %(max)s)'
1233 rows, idx = gmPG2.run_ro_queries (
1234 queries = [{
1235 'cmd': cmd,
1236 'args': {'dob': self['dob'], 'min': min_distance, 'max': max_distance}
1237 }]
1238 )
1239 return rows[0][0]
1240
1241
1242
1244 cmd = u'select * from clin.v_most_recent_encounters where pk_patient=%s'
1245 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self._payload[self._idx['pk_identity']]]}])
1246 if len(rows) > 0:
1247 return rows[0]
1248 else:
1249 return None
1250
1253
1254 messages = property(get_messages, lambda x:x)
1255
1258
1259 overdue_messages = property(_get_overdue_messages, lambda x:x)
1260
1263
1266
1267 dynamic_hints = property(_get_dynamic_hints, lambda x:x)
1268
1270 if self._payload[self._idx['pk_primary_provider']] is None:
1271 return None
1272 from Gnumed.business import gmStaff
1273 return gmStaff.cStaff(aPK_obj = self._payload[self._idx['pk_primary_provider']])
1274
1275 primary_provider = property(_get_primary_provider, lambda x:x)
1276
1277
1278
1280 """Format patient demographics into patient specific path name fragment."""
1281 return (u'%s-%s%s-%s' % (
1282 self._payload[self._idx['lastnames']].replace(u' ', u'_'),
1283 self._payload[self._idx['firstnames']].replace(u' ', u'_'),
1284 gmTools.coalesce(self._payload[self._idx['preferred']], u'', template_initial = u'-(%s)').replace(u' ', u'_'),
1285 self.get_formatted_dob(format = '%Y-%m-%d', encoding = gmI18N.get_encoding())
1286 )).replace (
1287 u"'", u""
1288 ).replace (
1289 u'"', u''
1290 ).replace (
1291 u'/', u'_'
1292 ).replace (
1293 u'\\', u'_'
1294 ).replace (
1295 u'~', u''
1296 ).replace (
1297 u'|', u'_'
1298 ).replace (
1299 u'*', u''
1300 ).replace (
1301 u'\u2248', u''
1302 )
1303
1304 dirname = property(get_dirname, lambda x:x)
1305
1309
1310 tray_dir_name = property(_get_tray_dir_name, lambda x:x)
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1328 """Represents a person which is a patient.
1329
1330 - a specializing subclass of cIdentity turning it into a patient
1331 - its use is to cache subobjects like EMR and document folder
1332 """
1333 - def __init__(self, aPK_obj=None, row=None):
1334 cIdentity.__init__(self, aPK_obj=aPK_obj, row=row)
1335 self.__db_cache = {}
1336 self.__emr_access_lock = threading.Lock()
1337
1339 """Do cleanups before dying.
1340
1341 - note that this may be called in a thread
1342 """
1343 if self.__db_cache.has_key('clinical record'):
1344 self.__db_cache['clinical record'].cleanup()
1345 if self.__db_cache.has_key('document folder'):
1346 self.__db_cache['document folder'].cleanup()
1347 cIdentity.cleanup(self)
1348
1349 - def get_emr(self, allow_user_interaction=True):
1350 if not self.__emr_access_lock.acquire(False):
1351
1352 _log.debug('failed to acquire EMR access lock, sleeping for 500ms')
1353 time.sleep(0.5)
1354 if not self.__emr_access_lock.acquire(False):
1355 _log.debug('still failed to acquire EMR access lock, aborting')
1356 raise AttributeError('cannot lock access to EMR')
1357 try:
1358 self.__db_cache['clinical record']
1359 except KeyError:
1360 self.__db_cache['clinical record'] = gmClinicalRecord.cClinicalRecord(aPKey = self._payload[self._idx['pk_identity']], allow_user_interaction = allow_user_interaction)
1361 self.__emr_access_lock.release()
1362 return self.__db_cache['clinical record']
1363
1364 emr = property(get_emr, lambda x:x)
1365
1367 try:
1368 return self.__db_cache['document folder']
1369 except KeyError:
1370 pass
1371
1372 self.__db_cache['document folder'] = cDocumentFolder(aPKey = self._payload[self._idx['pk_identity']])
1373 return self.__db_cache['document folder']
1374
1375 document_folder = property(get_document_folder, lambda x:x)
1376
1378 """Patient Borg to hold currently active patient.
1379
1380 There may be many instances of this but they all share state.
1381 """
1382 - def __init__(self, patient=None, forced_reload=False):
1383 """Change or get currently active patient.
1384
1385 patient:
1386 * None: get currently active patient
1387 * -1: unset currently active patient
1388 * cPatient instance: set active patient if possible
1389 """
1390
1391 try:
1392 tmp = self.patient
1393 except AttributeError:
1394 self.patient = gmNull.cNull()
1395 self.__register_interests()
1396
1397
1398
1399 self.__lock_depth = 0
1400
1401 self.__pre_selection_callbacks = []
1402
1403
1404 if patient is None:
1405 return None
1406
1407
1408 if self.locked:
1409 _log.error('patient [%s] is locked, cannot change to [%s]' % (self.patient['pk_identity'], patient))
1410 return None
1411
1412
1413 if patient == -1:
1414 _log.debug('explicitly unsetting current patient')
1415 if not self.__run_pre_selection_callbacks():
1416 _log.debug('not unsetting current patient')
1417 return None
1418 self.__send_pre_selection_notification()
1419 self.patient.cleanup()
1420 self.patient = gmNull.cNull()
1421 self.__send_selection_notification()
1422 return None
1423
1424
1425 if not isinstance(patient, cPatient):
1426 _log.error('cannot set active patient to [%s], must be either None, -1 or cPatient instance' % str(patient))
1427 raise TypeError, 'gmPerson.gmCurrentPatient.__init__(): <patient> must be None, -1 or cPatient instance but is: %s' % str(patient)
1428
1429
1430 if (self.patient['pk_identity'] == patient['pk_identity']) and not forced_reload:
1431 return None
1432
1433
1434 _log.debug('patient change [%s] -> [%s] requested', self.patient['pk_identity'], patient['pk_identity'])
1435
1436
1437 if not self.__run_pre_selection_callbacks():
1438 _log.debug('not changing current patient')
1439 return None
1440 self.__send_pre_selection_notification()
1441 self.patient.cleanup()
1442 self.patient = patient
1443 self.patient.get_emr()
1444 self.__send_selection_notification()
1445
1446 return None
1447
1451
1455
1456
1457
1459 if not callable(callback):
1460 raise TypeError(u'callback [%s] not callable' % callback)
1461
1462 self.__pre_selection_callbacks.append(callback)
1463
1466
1468 raise AttributeError(u'invalid to set <connected> state')
1469
1470 connected = property(_get_connected, _set_connected)
1471
1473 return (self.__lock_depth > 0)
1474
1476 if locked:
1477 self.__lock_depth = self.__lock_depth + 1
1478 gmDispatcher.send(signal='patient_locked')
1479 else:
1480 if self.__lock_depth == 0:
1481 _log.error('lock/unlock imbalance, trying to refcount lock depth below 0')
1482 return
1483 else:
1484 self.__lock_depth = self.__lock_depth - 1
1485 gmDispatcher.send(signal='patient_unlocked')
1486
1487 locked = property(_get_locked, _set_locked)
1488
1490 _log.info('forced patient unlock at lock depth [%s]' % self.__lock_depth)
1491 self.__lock_depth = 0
1492 gmDispatcher.send(signal='patient_unlocked')
1493
1494
1495
1497 if isinstance(self.patient, gmNull.cNull):
1498 return True
1499
1500 for call_back in self.__pre_selection_callbacks:
1501 try:
1502 successful = call_back()
1503 except:
1504 _log.exception('callback [%s] failed', call_back)
1505 print "*** pre-selection callback failed ***"
1506 print type(call_back)
1507 print call_back
1508 return False
1509
1510 if not successful:
1511 _log.debug('callback [%s] returned False', call_back)
1512 return False
1513
1514 return True
1515
1517 """Sends signal when another patient is about to become active.
1518
1519 This does NOT wait for signal handlers to complete.
1520 """
1521 kwargs = {
1522 'signal': u'pre_patient_selection',
1523 'sender': id(self.__class__),
1524 'pk_identity': self.patient['pk_identity']
1525 }
1526 gmDispatcher.send(**kwargs)
1527
1529 """Sends signal when another patient has actually been made active."""
1530 kwargs = {
1531 'signal': u'post_patient_selection',
1532 'sender': id(self.__class__),
1533 'pk_identity': self.patient['pk_identity']
1534 }
1535 gmDispatcher.send(**kwargs)
1536
1537
1538
1540 if attribute == 'patient':
1541 raise AttributeError
1542 if not isinstance(self.patient, gmNull.cNull):
1543 return getattr(self.patient, attribute)
1544
1545
1546
1548 """Return any attribute if known how to retrieve it by proxy.
1549 """
1550 return self.patient[attribute]
1551
1554
1555
1556
1559 gmMatchProvider.cMatchProvider_SQL2.__init__(
1560 self,
1561 queries = [
1562 u"""SELECT
1563 pk_staff AS data,
1564 short_alias || ' (' || coalesce(title, '') || ' ' || firstnames || ' ' || lastnames || ')' AS list_label,
1565 short_alias || ' (' || coalesce(title, '') || ' ' || firstnames || ' ' || lastnames || ')' AS field_label
1566 FROM dem.v_staff
1567 WHERE
1568 is_active AND (
1569 short_alias %(fragment_condition)s OR
1570 firstnames %(fragment_condition)s OR
1571 lastnames %(fragment_condition)s OR
1572 db_user %(fragment_condition)s
1573 )
1574 """
1575 ]
1576 )
1577 self.setThresholds(1, 2, 3)
1578
1579
1580
1581 -def create_name(pk_person, firstnames, lastnames, active=False):
1582 queries = [{
1583 'cmd': u"select dem.add_name(%s, %s, %s, %s)",
1584 'args': [pk_person, firstnames, lastnames, active]
1585 }]
1586 rows, idx = gmPG2.run_rw_queries(queries=queries, return_data=True)
1587 name = cPersonName(aPK_obj = rows[0][0])
1588 return name
1589
1590 -def create_identity(gender=None, dob=None, lastnames=None, firstnames=None):
1591
1592 cmd1 = u"""INSERT INTO dem.identity (gender, dob) VALUES (%s, %s)"""
1593 cmd2 = u"""
1594 INSERT INTO dem.names (
1595 id_identity, lastnames, firstnames
1596 ) VALUES (
1597 currval('dem.identity_pk_seq'), coalesce(%s, 'xxxDEFAULTxxx'), coalesce(%s, 'xxxDEFAULTxxx')
1598 ) RETURNING id_identity"""
1599 rows, idx = gmPG2.run_rw_queries (
1600 queries = [
1601 {'cmd': cmd1, 'args': [gender, dob]},
1602 {'cmd': cmd2, 'args': [lastnames, firstnames]}
1603 ],
1604 return_data = True
1605 )
1606 ident = cIdentity(aPK_obj=rows[0][0])
1607 gmHooks.run_hook_script(hook = u'post_person_creation')
1608 return ident
1609
1617
1619 """Set active patient.
1620
1621 If patient is -1 the active patient will be UNset.
1622 """
1623 if isinstance(patient, cPatient):
1624 pat = patient
1625 elif isinstance(patient, cIdentity):
1626 pat = cPatient(aPK_obj = patient['pk_identity'])
1627
1628
1629 elif isinstance(patient, gmCurrentPatient):
1630 pat = patient.patient
1631 elif patient == -1:
1632 pat = patient
1633 else:
1634
1635 success, pk = gmTools.input2int(initial = patient, minval = 1)
1636 if not success:
1637 raise ValueError('<patient> must be either -1, >0, or a cPatient, cIdentity or gmCurrentPatient instance, is: %s' % patient)
1638
1639 try:
1640 pat = cPatient(aPK_obj = pk)
1641 except:
1642 _log.exception('error changing active patient to [%s]' % patient)
1643 return False
1644
1645
1646 try:
1647 gmCurrentPatient(patient = pat, forced_reload = forced_reload)
1648 except:
1649 _log.exception('error changing active patient to [%s]' % patient)
1650 return False
1651
1652 return True
1653
1654
1655
1666
1667 map_gender2mf = {
1668 'm': u'm',
1669 'f': u'f',
1670 'tf': u'f',
1671 'tm': u'm',
1672 'h': u'mf'
1673 }
1674
1675
1676 map_gender2symbol = {
1677 'm': u'\u2642',
1678 'f': u'\u2640',
1679 'tf': u'\u26A5\u2640',
1680 'tm': u'\u26A5\u2642',
1681 'h': u'\u26A5'
1682
1683
1684
1685 }
1686
1706
1727
1729 """Try getting the gender for the given first name."""
1730
1731 if firstnames is None:
1732 return None
1733
1734 rows, idx = gmPG2.run_ro_queries(queries = [{
1735 'cmd': u"select gender from dem.name_gender_map where name ilike %(fn)s limit 1",
1736 'args': {'fn': firstnames}
1737 }])
1738
1739 if len(rows) == 0:
1740 return None
1741
1742 return rows[0][0]
1743
1745 return [ cIdentity(aPK_obj = pk) for pk in pks ]
1746
1750
1754
1755
1756
1757 if __name__ == '__main__':
1758
1759 if len(sys.argv) == 1:
1760 sys.exit()
1761
1762 if sys.argv[1] != 'test':
1763 sys.exit()
1764
1765 import datetime
1766
1767 gmI18N.activate_locale()
1768 gmI18N.install_domain()
1769 gmDateTime.init()
1770
1771
1792
1794 dto = cDTO_person()
1795 dto.firstnames = 'Sepp'
1796 dto.lastnames = 'Herberger'
1797 dto.gender = 'male'
1798 dto.dob = pyDT.datetime.now(tz=gmDateTime.gmCurrentLocalTimezone)
1799 print dto
1800
1801 print dto['firstnames']
1802 print dto['lastnames']
1803 print dto['gender']
1804 print dto['dob']
1805
1806 for key in dto.keys():
1807 print key
1808
1810
1811 print '\n\nCreating identity...'
1812 new_identity = create_identity(gender='m', dob='2005-01-01', lastnames='test lastnames', firstnames='test firstnames')
1813 print 'Identity created: %s' % new_identity
1814
1815 print '\nSetting title and gender...'
1816 new_identity['title'] = 'test title';
1817 new_identity['gender'] = 'f';
1818 new_identity.save_payload()
1819 print 'Refetching identity from db: %s' % cIdentity(aPK_obj=new_identity['pk_identity'])
1820
1821 print '\nGetting all names...'
1822 for a_name in new_identity.get_names():
1823 print a_name
1824 print 'Active name: %s' % (new_identity.get_active_name())
1825 print 'Setting nickname...'
1826 new_identity.set_nickname(nickname='test nickname')
1827 print 'Refetching all names...'
1828 for a_name in new_identity.get_names():
1829 print a_name
1830 print 'Active name: %s' % (new_identity.get_active_name())
1831
1832 print '\nIdentity occupations: %s' % new_identity['occupations']
1833 print 'Creating identity occupation...'
1834 new_identity.link_occupation('test occupation')
1835 print 'Identity occupations: %s' % new_identity['occupations']
1836
1837 print '\nIdentity addresses: %s' % new_identity.get_addresses()
1838 print 'Creating identity address...'
1839
1840 new_identity.link_address (
1841 number = 'test 1234',
1842 street = 'test street',
1843 postcode = 'test postcode',
1844 urb = 'test urb',
1845 state = 'SN',
1846 country = 'DE'
1847 )
1848 print 'Identity addresses: %s' % new_identity.get_addresses()
1849
1850 print '\nIdentity communications: %s' % new_identity.get_comm_channels()
1851 print 'Creating identity communication...'
1852 new_identity.link_comm_channel('homephone', '1234566')
1853 print 'Identity communications: %s' % new_identity.get_comm_channels()
1854
1860
1862 genders, idx = get_gender_list()
1863 print "\n\nRetrieving gender enum (tag, label, weight):"
1864 for gender in genders:
1865 print "%s, %s, %s" % (gender[idx['tag']], gender[idx['l10n_label']], gender[idx['sort_weight']])
1866
1867
1868
1869
1870
1871
1872 test_gender_list()
1873
1874
1875
1876
1877
1878
1879
1880
1881