Package Gnumed :: Package business :: Module gmPerson
[frames] | no frames]

Source Code for Module Gnumed.business.gmPerson

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