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