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
840 cmd_template = u'UPDATE %s SET %s = %%(pat2keep)s WHERE %s = %%(pat2del)s'
841 for FK in FKs:
842 if FK['referencing_table'] == u'dem.names':
843 continue
844 queries.append ({
845 'cmd': cmd_template % (FK['referencing_table'], FK['referencing_column'], FK['referencing_column']),
846 'args': args
847 })
848
849
850 queries.append ({
851 'cmd': u'delete from dem.identity where pk = %(pat2del)s',
852 'args': args
853 })
854
855 _log.warning('identity [%s] is about to assimilate identity [%s]', self.ID, other_identity.ID)
856
857 gmPG2.run_rw_queries(link_obj = link_obj, queries = queries, end_tx = True)
858
859 self.add_external_id (
860 type_name = u'merged GNUmed identity primary key',
861 value = u'GNUmed::pk::%s' % other_identity.ID,
862 issuer = u'GNUmed'
863 )
864
865 return True, None
866
867
869 cmd = u"""
870 insert into clin.waiting_list (fk_patient, urgency, comment, area, list_position)
871 values (
872 %(pat)s,
873 %(urg)s,
874 %(cmt)s,
875 %(area)s,
876 (select coalesce((max(list_position) + 1), 1) from clin.waiting_list)
877 )"""
878 args = {'pat': self.ID, 'urg': urgency, 'cmt': comment, 'area': zone}
879 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], verbose = True)
880
882 cmd = u"""SELECT * FROM clin.v_waiting_list WHERE pk_identity = %(pat)s"""
883 args = {'pat': self.ID}
884 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
885 return rows
886
887 waiting_list_entries = property(get_waiting_list_entry, lambda x:x)
888
892
893 export_tray = property(_get_export_tray, lambda x:x)
894
895 - def export_as_gdt(self, filename=None, encoding='iso-8859-15', external_id_type=None):
896
897 template = u'%s%s%s\r\n'
898
899 file = codecs.open (
900 filename = filename,
901 mode = 'wb',
902 encoding = encoding,
903 errors = 'strict'
904 )
905
906 file.write(template % (u'013', u'8000', u'6301'))
907 file.write(template % (u'013', u'9218', u'2.10'))
908 if external_id_type is None:
909 file.write(template % (u'%03d' % (9 + len(str(self.ID))), u'3000', self.ID))
910 else:
911 ext_ids = self.get_external_ids(id_type = external_id_type)
912 if len(ext_ids) > 0:
913 file.write(template % (u'%03d' % (9 + len(ext_ids[0]['value'])), u'3000', ext_ids[0]['value']))
914 file.write(template % (u'%03d' % (9 + len(self._payload[self._idx['lastnames']])), u'3101', self._payload[self._idx['lastnames']]))
915 file.write(template % (u'%03d' % (9 + len(self._payload[self._idx['firstnames']])), u'3102', self._payload[self._idx['firstnames']]))
916 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')))
917 file.write(template % (u'010', u'3110', gmXdtMappings.map_gender_gm2xdt[self._payload[self._idx['gender']]]))
918 file.write(template % (u'025', u'6330', 'GNUmed::9206::encoding'))
919 file.write(template % (u'%03d' % (9 + len(encoding)), u'6331', encoding))
920 if external_id_type is None:
921 file.write(template % (u'029', u'6332', u'GNUmed::3000::source'))
922 file.write(template % (u'017', u'6333', u'internal'))
923 else:
924 if len(ext_ids) > 0:
925 file.write(template % (u'029', u'6332', u'GNUmed::3000::source'))
926 file.write(template % (u'%03d' % (9 + len(external_id_type)), u'6333', external_id_type))
927
928 file.close()
929
930
931
934
936 """Link an occupation with a patient, creating the occupation if it does not exists.
937
938 @param occupation The name of the occupation to link the patient to.
939 """
940 if (activities is None) and (occupation is None):
941 return True
942
943 occupation = occupation.strip()
944 if len(occupation) == 0:
945 return True
946
947 if activities is not None:
948 activities = activities.strip()
949
950 args = {'act': activities, 'pat_id': self.pk_obj, 'job': occupation}
951
952 cmd = u"select activities from dem.v_person_jobs where pk_identity = %(pat_id)s and l10n_occupation = _(%(job)s)"
953 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
954
955 queries = []
956 if len(rows) == 0:
957 queries.append ({
958 'cmd': u"INSERT INTO dem.lnk_job2person (fk_identity, fk_occupation, activities) VALUES (%(pat_id)s, dem.create_occupation(%(job)s), %(act)s)",
959 'args': args
960 })
961 else:
962 if rows[0]['activities'] != activities:
963 queries.append ({
964 '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))",
965 'args': args
966 })
967
968 rows, idx = gmPG2.run_rw_queries(queries = queries)
969
970 return True
971
973 if occupation is None:
974 return True
975 occupation = occupation.strip()
976 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))"
977 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj, 'job': occupation}}])
978 return True
979
980
981
983 cmd = u"select * from dem.v_person_comms where pk_identity = %s"
984 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}], get_col_idx = True)
985
986 filtered = rows
987
988 if comm_medium is not None:
989 filtered = []
990 for row in rows:
991 if row['comm_type'] == comm_medium:
992 filtered.append(row)
993
994 return [ gmDemographicRecord.cCommChannel(row = {
995 'pk_field': 'pk_lnk_identity2comm',
996 'data': r,
997 'idx': idx
998 }) for r in filtered
999 ]
1000
1001 - def link_comm_channel(self, comm_medium=None, url=None, is_confidential=False, pk_channel_type=None):
1002 """Link a communication medium with a patient.
1003
1004 @param comm_medium The name of the communication medium.
1005 @param url The communication resource locator.
1006 @type url A types.StringType instance.
1007 @param is_confidential Wether the data must be treated as confidential.
1008 @type is_confidential A types.BooleanType instance.
1009 """
1010 comm_channel = gmDemographicRecord.create_comm_channel (
1011 comm_medium = comm_medium,
1012 url = url,
1013 is_confidential = is_confidential,
1014 pk_channel_type = pk_channel_type,
1015 pk_identity = self.pk_obj
1016 )
1017 return comm_channel
1018
1024
1025
1026
1028
1029 cmd = u"SELECT * FROM dem.v_pat_addresses WHERE pk_identity = %(pat)s"
1030 args = {'pat': self.pk_obj}
1031 if address_type is not None:
1032 cmd = cmd + u" AND address_type = %(typ)s"
1033 args['typ'] = address_type
1034
1035 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
1036
1037 return [
1038 gmDemographicRecord.cPatientAddress(row = {'idx': idx, 'data': r, 'pk_field': 'pk_address'})
1039 for r in rows
1040 ]
1041
1042 - 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):
1043 """Link an address with a patient, creating the address if it does not exists.
1044
1045 @param id_type The primary key of the address type.
1046 """
1047 if address is None:
1048 address = gmDemographicRecord.create_address (
1049 country = country,
1050 state = state,
1051 urb = urb,
1052 suburb = suburb,
1053 postcode = postcode,
1054 street = street,
1055 number = number,
1056 subunit = subunit
1057 )
1058
1059 if address is None:
1060 return None
1061
1062
1063 cmd = u"SELECT id_address FROM dem.lnk_person_org_address WHERE id_identity = %(pat)s AND id_address = %(adr)s"
1064 args = {'pat': self.pk_obj, 'adr': address['pk_address']}
1065 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
1066
1067
1068 if len(rows) == 0:
1069 args = {'id': self.pk_obj, 'adr': address['pk_address'], 'type': id_type}
1070 cmd = u"""
1071 INSERT INTO dem.lnk_person_org_address(id_identity, id_address)
1072 VALUES (%(id)s, %(adr)s)
1073 RETURNING id_address"""
1074 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
1075
1076 linked_adr = gmDemographicRecord.cPatientAddress(aPK_obj = rows[0]['id_address'])
1077
1078
1079 if id_type is not None:
1080 linked_adr['pk_address_type'] = id_type
1081 linked_adr.save()
1082
1083 return linked_adr
1084
1086 """Remove an address from the patient.
1087
1088 The address itself stays in the database.
1089 The address can be either cAdress or cPatientAdress.
1090 """
1091 if pk_address is None:
1092 args = {'person': self.pk_obj, 'adr': address['pk_address']}
1093 else:
1094 args = {'person': self.pk_obj, 'adr': pk_address}
1095 cmd = u"DELETE FROM dem.lnk_person_org_address WHERE id_identity = %(person)s AND id_address = %(adr)s"
1096 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1097
1098
1099
1101 cmd = u"""
1102 select
1103 t.description,
1104 vbp.pk_identity as id,
1105 title,
1106 firstnames,
1107 lastnames,
1108 dob,
1109 cob,
1110 gender,
1111 karyotype,
1112 pupic,
1113 pk_marital_status,
1114 marital_status,
1115 xmin_identity,
1116 preferred
1117 from
1118 dem.v_basic_person vbp, dem.relation_types t, dem.lnk_person2relative l
1119 where
1120 (
1121 l.id_identity = %(pk)s and
1122 vbp.pk_identity = l.id_relative and
1123 t.id = l.id_relation_type
1124 ) or (
1125 l.id_relative = %(pk)s and
1126 vbp.pk_identity = l.id_identity and
1127 t.inverse = l.id_relation_type
1128 )"""
1129 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj}}])
1130 if len(rows) == 0:
1131 return []
1132 return [(row[0], cIdentity(row = {'data': row[1:], 'idx':idx, 'pk_field': 'pk'})) for row in rows]
1133
1135
1136 id_new_relative = create_dummy_identity()
1137
1138 relative = cIdentity(aPK_obj=id_new_relative)
1139
1140
1141 relative.add_name( '**?**', self.get_names()['lastnames'])
1142
1143 if self._ext_cache.has_key('relatives'):
1144 del self._ext_cache['relatives']
1145 cmd = u"""
1146 insert into dem.lnk_person2relative (
1147 id_identity, id_relative, id_relation_type
1148 ) values (
1149 %s, %s, (select id from dem.relation_types where description = %s)
1150 )"""
1151 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': [self.ID, id_new_relative, rel_type ]}])
1152 return True
1153
1155
1156 self.set_relative(None, relation)
1157
1162
1163 emergency_contact_in_database = property(_get_emergency_contact_from_database, lambda x:x)
1164
1165
1166
1175
1216
1217 - def dob_in_range(self, min_distance=u'1 week', max_distance=u'1 week'):
1218 cmd = u'select dem.dob_is_in_range(%(dob)s, %(min)s, %(max)s)'
1219 rows, idx = gmPG2.run_ro_queries (
1220 queries = [{
1221 'cmd': cmd,
1222 'args': {'dob': self['dob'], 'min': min_distance, 'max': max_distance}
1223 }]
1224 )
1225 return rows[0][0]
1226
1227
1228
1230 cmd = u'select * from clin.v_most_recent_encounters where pk_patient=%s'
1231 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self._payload[self._idx['pk_identity']]]}])
1232 if len(rows) > 0:
1233 return rows[0]
1234 else:
1235 return None
1236
1239
1240 messages = property(get_messages, lambda x:x)
1241
1244
1245 overdue_messages = property(_get_overdue_messages, lambda x:x)
1246
1249
1252
1253 dynamic_hints = property(_get_dynamic_hints, lambda x:x)
1254
1256 if self._payload[self._idx['pk_primary_provider']] is None:
1257 return None
1258 from Gnumed.business import gmStaff
1259 return gmStaff.cStaff(aPK_obj = self._payload[self._idx['pk_primary_provider']])
1260
1261 primary_provider = property(_get_primary_provider, lambda x:x)
1262
1263
1264
1266 """Format patient demographics into patient specific path name fragment."""
1267 return (u'%s-%s%s-%s' % (
1268 self._payload[self._idx['lastnames']].replace(u' ', u'_'),
1269 self._payload[self._idx['firstnames']].replace(u' ', u'_'),
1270 gmTools.coalesce(self._payload[self._idx['preferred']], u'', template_initial = u'-(%s)').replace(u' ', u'_'),
1271 self.get_formatted_dob(format = '%Y-%m-%d', encoding = gmI18N.get_encoding())
1272 )).replace (
1273 u"'", u""
1274 ).replace (
1275 u'"', u''
1276 ).replace (
1277 u'/', u'_'
1278 ).replace (
1279 u'\\', u'_'
1280 ).replace (
1281 u'~', u''
1282 ).replace (
1283 u'|', u'_'
1284 ).replace (
1285 u'*', u''
1286 ).replace (
1287 u'\u2248', u''
1288 )
1289
1290 dirname = property(get_dirname, lambda x:x)
1291
1295
1296 tray_dir_name = property(_get_tray_dir_name, lambda x:x)
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1314 """Represents a person which is a patient.
1315
1316 - a specializing subclass of cIdentity turning it into a patient
1317 - its use is to cache subobjects like EMR and document folder
1318 """
1319 - def __init__(self, aPK_obj=None, row=None):
1320 cIdentity.__init__(self, aPK_obj=aPK_obj, row=row)
1321 self.__db_cache = {}
1322 self.__emr_access_lock = threading.Lock()
1323
1325 """Do cleanups before dying.
1326
1327 - note that this may be called in a thread
1328 """
1329 if self.__db_cache.has_key('clinical record'):
1330 self.__db_cache['clinical record'].cleanup()
1331 if self.__db_cache.has_key('document folder'):
1332 self.__db_cache['document folder'].cleanup()
1333 cIdentity.cleanup(self)
1334
1335 - def get_emr(self, allow_user_interaction=True):
1336 if not self.__emr_access_lock.acquire(False):
1337
1338 _log.debug('failed to acquire EMR access lock, sleeping for 500ms')
1339 time.sleep(0.5)
1340 if not self.__emr_access_lock.acquire(False):
1341 _log.debug('still failed to acquire EMR access lock, aborting')
1342 raise AttributeError('cannot lock access to EMR')
1343 try:
1344 self.__db_cache['clinical record']
1345 except KeyError:
1346 self.__db_cache['clinical record'] = gmClinicalRecord.cClinicalRecord(aPKey = self._payload[self._idx['pk_identity']], allow_user_interaction = allow_user_interaction)
1347 self.__emr_access_lock.release()
1348 return self.__db_cache['clinical record']
1349
1350 emr = property(get_emr, lambda x:x)
1351
1353 try:
1354 return self.__db_cache['document folder']
1355 except KeyError:
1356 pass
1357
1358 self.__db_cache['document folder'] = cDocumentFolder(aPKey = self._payload[self._idx['pk_identity']])
1359 return self.__db_cache['document folder']
1360
1361 document_folder = property(get_document_folder, lambda x:x)
1362
1364 """Patient Borg to hold currently active patient.
1365
1366 There may be many instances of this but they all share state.
1367 """
1368 - def __init__(self, patient=None, forced_reload=False):
1369 """Change or get currently active patient.
1370
1371 patient:
1372 * None: get currently active patient
1373 * -1: unset currently active patient
1374 * cPatient instance: set active patient if possible
1375 """
1376
1377 try:
1378 tmp = self.patient
1379 except AttributeError:
1380 self.patient = gmNull.cNull()
1381 self.__register_interests()
1382
1383
1384
1385 self.__lock_depth = 0
1386
1387 self.__pre_selection_callbacks = []
1388
1389
1390 if patient is None:
1391 return None
1392
1393
1394 if self.locked:
1395 _log.error('patient [%s] is locked, cannot change to [%s]' % (self.patient['pk_identity'], patient))
1396 return None
1397
1398
1399 if patient == -1:
1400 _log.debug('explicitly unsetting current patient')
1401 if not self.__run_pre_selection_callbacks():
1402 _log.debug('not unsetting current patient')
1403 return None
1404 self.__send_pre_selection_notification()
1405 self.patient.cleanup()
1406 self.patient = gmNull.cNull()
1407 self.__send_selection_notification()
1408 return None
1409
1410
1411 if not isinstance(patient, cPatient):
1412 _log.error('cannot set active patient to [%s], must be either None, -1 or cPatient instance' % str(patient))
1413 raise TypeError, 'gmPerson.gmCurrentPatient.__init__(): <patient> must be None, -1 or cPatient instance but is: %s' % str(patient)
1414
1415
1416 if (self.patient['pk_identity'] == patient['pk_identity']) and not forced_reload:
1417 return None
1418
1419
1420 _log.debug('patient change [%s] -> [%s] requested', self.patient['pk_identity'], patient['pk_identity'])
1421
1422
1423 if not self.__run_pre_selection_callbacks():
1424 _log.debug('not changing current patient')
1425 return None
1426 self.__send_pre_selection_notification()
1427 self.patient.cleanup()
1428 self.patient = patient
1429 self.patient.get_emr()
1430 self.__send_selection_notification()
1431
1432 return None
1433
1437
1441
1442
1443
1445 if not callable(callback):
1446 raise TypeError(u'callback [%s] not callable' % callback)
1447
1448 self.__pre_selection_callbacks.append(callback)
1449
1452
1454 raise AttributeError(u'invalid to set <connected> state')
1455
1456 connected = property(_get_connected, _set_connected)
1457
1459 return (self.__lock_depth > 0)
1460
1462 if locked:
1463 self.__lock_depth = self.__lock_depth + 1
1464 gmDispatcher.send(signal='patient_locked')
1465 else:
1466 if self.__lock_depth == 0:
1467 _log.error('lock/unlock imbalance, trying to refcount lock depth below 0')
1468 return
1469 else:
1470 self.__lock_depth = self.__lock_depth - 1
1471 gmDispatcher.send(signal='patient_unlocked')
1472
1473 locked = property(_get_locked, _set_locked)
1474
1476 _log.info('forced patient unlock at lock depth [%s]' % self.__lock_depth)
1477 self.__lock_depth = 0
1478 gmDispatcher.send(signal='patient_unlocked')
1479
1480
1481
1483 if isinstance(self.patient, gmNull.cNull):
1484 return True
1485
1486 for call_back in self.__pre_selection_callbacks:
1487 try:
1488 successful = call_back()
1489 except:
1490 _log.exception('callback [%s] failed', call_back)
1491 print "*** pre-selection callback failed ***"
1492 print type(call_back)
1493 print call_back
1494 return False
1495
1496 if not successful:
1497 _log.debug('callback [%s] returned False', call_back)
1498 return False
1499
1500 return True
1501
1503 """Sends signal when another patient is about to become active.
1504
1505 This does NOT wait for signal handlers to complete.
1506 """
1507 kwargs = {
1508 'signal': u'pre_patient_selection',
1509 'sender': id(self.__class__),
1510 'pk_identity': self.patient['pk_identity']
1511 }
1512 gmDispatcher.send(**kwargs)
1513
1515 """Sends signal when another patient has actually been made active."""
1516 kwargs = {
1517 'signal': u'post_patient_selection',
1518 'sender': id(self.__class__),
1519 'pk_identity': self.patient['pk_identity']
1520 }
1521 gmDispatcher.send(**kwargs)
1522
1523
1524
1526 if attribute == 'patient':
1527 raise AttributeError
1528 if not isinstance(self.patient, gmNull.cNull):
1529 return getattr(self.patient, attribute)
1530
1531
1532
1534 """Return any attribute if known how to retrieve it by proxy.
1535 """
1536 return self.patient[attribute]
1537
1540
1541
1542
1545 gmMatchProvider.cMatchProvider_SQL2.__init__(
1546 self,
1547 queries = [
1548 u"""SELECT
1549 pk_staff AS data,
1550 short_alias || ' (' || coalesce(title, '') || ' ' || firstnames || ' ' || lastnames || ')' AS list_label,
1551 short_alias || ' (' || coalesce(title, '') || ' ' || firstnames || ' ' || lastnames || ')' AS field_label
1552 FROM dem.v_staff
1553 WHERE
1554 is_active AND (
1555 short_alias %(fragment_condition)s OR
1556 firstnames %(fragment_condition)s OR
1557 lastnames %(fragment_condition)s OR
1558 db_user %(fragment_condition)s
1559 )
1560 """
1561 ]
1562 )
1563 self.setThresholds(1, 2, 3)
1564
1565
1566
1567 -def create_name(pk_person, firstnames, lastnames, active=False):
1568 queries = [{
1569 'cmd': u"select dem.add_name(%s, %s, %s, %s)",
1570 'args': [pk_person, firstnames, lastnames, active]
1571 }]
1572 rows, idx = gmPG2.run_rw_queries(queries=queries, return_data=True)
1573 name = cPersonName(aPK_obj = rows[0][0])
1574 return name
1575
1576 -def create_identity(gender=None, dob=None, lastnames=None, firstnames=None):
1577
1578 cmd1 = u"""INSERT INTO dem.identity (gender, dob) VALUES (%s, %s)"""
1579 cmd2 = u"""
1580 INSERT INTO dem.names (
1581 id_identity, lastnames, firstnames
1582 ) VALUES (
1583 currval('dem.identity_pk_seq'), coalesce(%s, 'xxxDEFAULTxxx'), coalesce(%s, 'xxxDEFAULTxxx')
1584 ) RETURNING id_identity"""
1585 rows, idx = gmPG2.run_rw_queries (
1586 queries = [
1587 {'cmd': cmd1, 'args': [gender, dob]},
1588 {'cmd': cmd2, 'args': [lastnames, firstnames]}
1589 ],
1590 return_data = True
1591 )
1592 ident = cIdentity(aPK_obj=rows[0][0])
1593 gmHooks.run_hook_script(hook = u'post_person_creation')
1594 return ident
1595
1603
1605 """Set active patient.
1606
1607 If patient is -1 the active patient will be UNset.
1608 """
1609 if isinstance(patient, cPatient):
1610 pat = patient
1611 elif isinstance(patient, cIdentity):
1612 pat = cPatient(aPK_obj = patient['pk_identity'])
1613
1614
1615 elif isinstance(patient, gmCurrentPatient):
1616 pat = patient.patient
1617 elif patient == -1:
1618 pat = patient
1619 else:
1620
1621 success, pk = gmTools.input2int(initial = patient, minval = 1)
1622 if not success:
1623 raise ValueError('<patient> must be either -1, >0, or a cPatient, cIdentity or gmCurrentPatient instance, is: %s' % patient)
1624
1625 try:
1626 pat = cPatient(aPK_obj = pk)
1627 except:
1628 _log.exception('error changing active patient to [%s]' % patient)
1629 return False
1630
1631
1632 try:
1633 gmCurrentPatient(patient = pat, forced_reload = forced_reload)
1634 except:
1635 _log.exception('error changing active patient to [%s]' % patient)
1636 return False
1637
1638 return True
1639
1640
1641
1652
1653 map_gender2mf = {
1654 'm': u'm',
1655 'f': u'f',
1656 'tf': u'f',
1657 'tm': u'm',
1658 'h': u'mf'
1659 }
1660
1661
1662 map_gender2symbol = {
1663 'm': u'\u2642',
1664 'f': u'\u2640',
1665 'tf': u'\u26A5\u2640',
1666 'tm': u'\u26A5\u2642',
1667 'h': u'\u26A5'
1668
1669
1670
1671 }
1672
1692
1713
1715 """Try getting the gender for the given first name."""
1716
1717 if firstnames is None:
1718 return None
1719
1720 rows, idx = gmPG2.run_ro_queries(queries = [{
1721 'cmd': u"select gender from dem.name_gender_map where name ilike %(fn)s limit 1",
1722 'args': {'fn': firstnames}
1723 }])
1724
1725 if len(rows) == 0:
1726 return None
1727
1728 return rows[0][0]
1729
1731 return [ cIdentity(aPK_obj = pk) for pk in pks ]
1732
1736
1740
1741
1742
1743 if __name__ == '__main__':
1744
1745 if len(sys.argv) == 1:
1746 sys.exit()
1747
1748 if sys.argv[1] != 'test':
1749 sys.exit()
1750
1751 import datetime
1752
1753 gmI18N.activate_locale()
1754 gmI18N.install_domain()
1755 gmDateTime.init()
1756
1757
1778
1780 dto = cDTO_person()
1781 dto.firstnames = 'Sepp'
1782 dto.lastnames = 'Herberger'
1783 dto.gender = 'male'
1784 dto.dob = pyDT.datetime.now(tz=gmDateTime.gmCurrentLocalTimezone)
1785 print dto
1786
1787 print dto['firstnames']
1788 print dto['lastnames']
1789 print dto['gender']
1790 print dto['dob']
1791
1792 for key in dto.keys():
1793 print key
1794
1796
1797 print '\n\nCreating identity...'
1798 new_identity = create_identity(gender='m', dob='2005-01-01', lastnames='test lastnames', firstnames='test firstnames')
1799 print 'Identity created: %s' % new_identity
1800
1801 print '\nSetting title and gender...'
1802 new_identity['title'] = 'test title';
1803 new_identity['gender'] = 'f';
1804 new_identity.save_payload()
1805 print 'Refetching identity from db: %s' % cIdentity(aPK_obj=new_identity['pk_identity'])
1806
1807 print '\nGetting all names...'
1808 for a_name in new_identity.get_names():
1809 print a_name
1810 print 'Active name: %s' % (new_identity.get_active_name())
1811 print 'Setting nickname...'
1812 new_identity.set_nickname(nickname='test nickname')
1813 print 'Refetching all names...'
1814 for a_name in new_identity.get_names():
1815 print a_name
1816 print 'Active name: %s' % (new_identity.get_active_name())
1817
1818 print '\nIdentity occupations: %s' % new_identity['occupations']
1819 print 'Creating identity occupation...'
1820 new_identity.link_occupation('test occupation')
1821 print 'Identity occupations: %s' % new_identity['occupations']
1822
1823 print '\nIdentity addresses: %s' % new_identity.get_addresses()
1824 print 'Creating identity address...'
1825
1826 new_identity.link_address (
1827 number = 'test 1234',
1828 street = 'test street',
1829 postcode = 'test postcode',
1830 urb = 'test urb',
1831 state = 'SN',
1832 country = 'DE'
1833 )
1834 print 'Identity addresses: %s' % new_identity.get_addresses()
1835
1836 print '\nIdentity communications: %s' % new_identity.get_comm_channels()
1837 print 'Creating identity communication...'
1838 new_identity.link_comm_channel('homephone', '1234566')
1839 print 'Identity communications: %s' % new_identity.get_comm_channels()
1840
1846
1848 genders, idx = get_gender_list()
1849 print "\n\nRetrieving gender enum (tag, label, weight):"
1850 for gender in genders:
1851 print "%s, %s, %s" % (gender[idx['tag']], gender[idx['l10n_label']], gender[idx['sort_weight']])
1852
1853
1854
1855
1856
1857
1858 test_gender_list()
1859
1860
1861
1862
1863
1864
1865
1866
1867