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