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