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 for name in self.get_names(): 425 if name['active_name'] is True: 426 return name 427 428 _log.error('cannot retrieve active name for patient [%s]' % self._payload[self._idx['pk_identity']]) 429 return None
430 #--------------------------------------------------------
431 - def get_names(self):
432 cmd = u""" 433 SELECT * 434 FROM dem.v_person_names 435 WHERE pk_identity = %(pk_pat)s 436 ORDER BY active_name DESC, lastnames, firstnames 437 """ 438 args = {'pk_pat': self._payload[self._idx['pk_identity']]} 439 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 440 441 if len(rows) == 0: 442 # no names registered for patient 443 return [] 444 445 names = [ cPersonName(row = {'idx': idx, 'data': r, 'pk_field': 'pk_name'}) for r in rows ] 446 return names
447 #--------------------------------------------------------
448 - def get_formatted_dob(self, format='%x', encoding=None, none_string=None):
449 return gmDateTime.format_dob ( 450 self._payload[self._idx['dob']], 451 format = format, 452 encoding = encoding, 453 none_string = none_string 454 )
455 #--------------------------------------------------------
456 - def get_description_gender(self):
457 return _(u'%(last)s,%(title)s %(first)s%(nick)s (%(sex)s)') % { 458 'last': self._payload[self._idx['lastnames']], 459 'title': gmTools.coalesce(self._payload[self._idx['title']], u'', u' %s'), 460 'first': self._payload[self._idx['firstnames']], 461 'nick': gmTools.coalesce(self._payload[self._idx['preferred']], u'', u" '%s'"), 462 'sex': self.gender_symbol 463 }
464 #--------------------------------------------------------
465 - def get_description(self):
466 return _(u'%(last)s,%(title)s %(first)s%(nick)s') % { 467 'last': self._payload[self._idx['lastnames']], 468 'title': gmTools.coalesce(self._payload[self._idx['title']], u'', u' %s'), 469 'first': self._payload[self._idx['firstnames']], 470 'nick': gmTools.coalesce(self._payload[self._idx['preferred']], u'', u" '%s'") 471 }
472 #--------------------------------------------------------
473 - def add_name(self, firstnames, lastnames, active=True):
474 """Add a name. 475 476 @param firstnames The first names. 477 @param lastnames The last names. 478 @param active When True, the new name will become the active one (hence setting other names to inactive) 479 @type active A types.BooleanType instance 480 """ 481 name = create_name(self.ID, firstnames, lastnames, active) 482 if active: 483 self.refetch_payload() 484 return name
485 #--------------------------------------------------------
486 - def delete_name(self, name=None):
487 cmd = u"delete from dem.names where id = %(name)s and id_identity = %(pat)s" 488 args = {'name': name['pk_name'], 'pat': self.ID} 489 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
490 # can't have been the active name as that would raise an 491 # exception (since no active name would be left) so no 492 # data refetch needed 493 #--------------------------------------------------------
494 - def set_nickname(self, nickname=None):
495 """ 496 Set the nickname. Setting the nickname only makes sense for the currently 497 active name. 498 @param nickname The preferred/nick/warrior name to set. 499 """ 500 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': u"select dem.set_nickname(%s, %s)", 'args': [self.ID, nickname]}]) 501 self.refetch_payload() 502 return True
503 #--------------------------------------------------------
504 - def get_tags(self, order_by=None):
505 if order_by is None: 506 order_by = u'' 507 else: 508 order_by = u'ORDER BY %s' % order_by 509 510 cmd = gmDemographicRecord._SQL_get_identity_tags % (u'pk_identity = %%(pat)s %s' % order_by) 511 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {u'pat': self.ID}}], get_col_idx = True) 512 513 return [ gmDemographicRecord.cIdentityTag(row = {'data': r, 'idx': idx, 'pk_field': 'pk_identity_tag'}) for r in rows ]
514 #--------------------------------------------------------
515 - def add_tag(self, tag):
516 args = { 517 u'tag': tag, 518 u'identity': self.ID 519 } 520 521 # already exists ? 522 cmd = u"SELECT pk FROM dem.identity_tag WHERE fk_tag = %(tag)s AND fk_identity = %(identity)s" 523 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 524 if len(rows) > 0: 525 return gmDemographicRecord.cIdentityTag(aPK_obj = rows[0]['pk']) 526 527 # no, add 528 cmd = u""" 529 INSERT INTO dem.identity_tag ( 530 fk_tag, 531 fk_identity 532 ) VALUES ( 533 %(tag)s, 534 %(identity)s 535 ) 536 RETURNING pk 537 """ 538 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False) 539 return gmDemographicRecord.cIdentityTag(aPK_obj = rows[0]['pk'])
540 #--------------------------------------------------------
541 - def remove_tag(self, tag):
542 cmd = u"DELETE FROM dem.identity_tag WHERE pk = %(pk)s" 543 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': tag}}])
544 #-------------------------------------------------------- 545 # external ID API 546 # 547 # since external IDs are not treated as first class 548 # citizens (classes in their own right, that is), we 549 # handle them *entirely* within cIdentity, also they 550 # only make sense with one single person (like names) 551 # and are not reused (like addresses), so they are 552 # truly added/deleted, not just linked/unlinked 553 #--------------------------------------------------------
554 - def add_external_id(self, type_name=None, value=None, issuer=None, comment=None, pk_type=None):
555 """Adds an external ID to the patient. 556 557 creates ID type if necessary 558 """ 559 560 # check for existing ID 561 if pk_type is not None: 562 cmd = u""" 563 select * from dem.v_external_ids4identity where 564 pk_identity = %(pat)s and 565 pk_type = %(pk_type)s and 566 value = %(val)s""" 567 else: 568 # by type/value/issuer 569 if issuer is None: 570 cmd = u""" 571 select * from dem.v_external_ids4identity where 572 pk_identity = %(pat)s and 573 name = %(name)s and 574 value = %(val)s""" 575 else: 576 cmd = u""" 577 select * from dem.v_external_ids4identity where 578 pk_identity = %(pat)s and 579 name = %(name)s and 580 value = %(val)s and 581 issuer = %(issuer)s""" 582 args = { 583 'pat': self.ID, 584 'name': type_name, 585 'val': value, 586 'issuer': issuer, 587 'pk_type': pk_type 588 } 589 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}]) 590 591 # create new ID if not found 592 if len(rows) == 0: 593 594 args = { 595 'pat': self.ID, 596 'val': value, 597 'type_name': type_name, 598 'pk_type': pk_type, 599 'issuer': issuer, 600 'comment': comment 601 } 602 603 if pk_type is None: 604 cmd = u"""insert into dem.lnk_identity2ext_id (external_id, fk_origin, comment, id_identity) values ( 605 %(val)s, 606 (select dem.add_external_id_type(%(type_name)s, %(issuer)s)), 607 %(comment)s, 608 %(pat)s 609 )""" 610 else: 611 cmd = u"""insert into dem.lnk_identity2ext_id (external_id, fk_origin, comment, id_identity) values ( 612 %(val)s, 613 %(pk_type)s, 614 %(comment)s, 615 %(pat)s 616 )""" 617 618 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 619 620 # or update comment of existing ID 621 else: 622 row = rows[0] 623 if comment is not None: 624 # comment not already there ? 625 if gmTools.coalesce(row['comment'], '').find(comment.strip()) == -1: 626 comment = '%s%s' % (gmTools.coalesce(row['comment'], '', '%s // '), comment.strip) 627 cmd = u"update dem.lnk_identity2ext_id set comment = %(comment)s where id=%(pk)s" 628 args = {'comment': comment, 'pk': row['pk_id']} 629 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
630 #--------------------------------------------------------
631 - def update_external_id(self, pk_id=None, type=None, value=None, issuer=None, comment=None):
632 """Edits an existing external ID. 633 634 Creates ID type if necessary. 635 """ 636 cmd = u""" 637 UPDATE dem.lnk_identity2ext_id SET 638 fk_origin = (SELECT dem.add_external_id_type(%(type)s, %(issuer)s)), 639 external_id = %(value)s, 640 comment = gm.nullify_empty_string(%(comment)s) 641 WHERE 642 id = %(pk)s 643 """ 644 args = {'pk': pk_id, 'value': value, 'type': type, 'issuer': issuer, 'comment': comment} 645 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
646 #--------------------------------------------------------
647 - def get_external_ids(self, id_type=None, issuer=None):
648 where_parts = ['pk_identity = %(pat)s'] 649 args = {'pat': self.ID} 650 651 if id_type is not None: 652 where_parts.append(u'name = %(name)s') 653 args['name'] = id_type.strip() 654 655 if issuer is not None: 656 where_parts.append(u'issuer = %(issuer)s') 657 args['issuer'] = issuer.strip() 658 659 cmd = u"SELECT * FROM dem.v_external_ids4identity WHERE %s" % ' AND '.join(where_parts) 660 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}]) 661 662 return rows
663 #--------------------------------------------------------
664 - def delete_external_id(self, pk_ext_id=None):
665 cmd = u""" 666 delete from dem.lnk_identity2ext_id 667 where id_identity = %(pat)s and id = %(pk)s""" 668 args = {'pat': self.ID, 'pk': pk_ext_id} 669 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
670 #--------------------------------------------------------
671 - def assimilate_identity(self, other_identity=None, link_obj=None):
672 """Merge another identity into this one. 673 674 Keep this one. Delete other one.""" 675 676 if other_identity.ID == self.ID: 677 return True, None 678 679 curr_pat = gmCurrentPatient() 680 if curr_pat.connected: 681 if other_identity.ID == curr_pat.ID: 682 return False, _('Cannot merge active patient into another patient.') 683 684 queries = [] 685 args = {'old_pat': other_identity.ID, 'new_pat': self.ID} 686 687 # delete old allergy state 688 queries.append ({ 689 '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)', 690 'args': args 691 }) 692 # FIXME: adjust allergy_state in kept patient 693 694 # deactivate all names of old patient 695 queries.append ({ 696 'cmd': u'update dem.names set active = False where id_identity = %(old_pat)s', 697 'args': args 698 }) 699 700 # find FKs pointing to identity 701 FKs = gmPG2.get_foreign_keys2column ( 702 schema = u'dem', 703 table = u'identity', 704 column = u'pk' 705 ) 706 707 # generate UPDATEs 708 cmd_template = u'update %s set %s = %%(new_pat)s where %s = %%(old_pat)s' 709 for FK in FKs: 710 queries.append ({ 711 'cmd': cmd_template % (FK['referencing_table'], FK['referencing_column'], FK['referencing_column']), 712 'args': args 713 }) 714 715 # remove old identity entry 716 queries.append ({ 717 'cmd': u'delete from dem.identity where pk = %(old_pat)s', 718 'args': args 719 }) 720 721 _log.warning('identity [%s] is about to assimilate identity [%s]', self.ID, other_identity.ID) 722 723 gmPG2.run_rw_queries(link_obj = link_obj, queries = queries, end_tx = True) 724 725 self.add_external_id ( 726 type_name = u'merged GNUmed identity primary key', 727 value = u'GNUmed::pk::%s' % other_identity.ID, 728 issuer = u'GNUmed' 729 ) 730 731 return True, None
732 #-------------------------------------------------------- 733 #--------------------------------------------------------
734 - def put_on_waiting_list(self, urgency=0, comment=None, zone=None):
735 cmd = u""" 736 insert into clin.waiting_list (fk_patient, urgency, comment, area, list_position) 737 values ( 738 %(pat)s, 739 %(urg)s, 740 %(cmt)s, 741 %(area)s, 742 (select coalesce((max(list_position) + 1), 1) from clin.waiting_list) 743 )""" 744 args = {'pat': self.ID, 'urg': urgency, 'cmt': comment, 'area': zone} 745 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], verbose=True)
746 #--------------------------------------------------------
747 - def export_as_gdt(self, filename=None, encoding='iso-8859-15', external_id_type=None):
748 749 template = u'%s%s%s\r\n' 750 751 file = codecs.open ( 752 filename = filename, 753 mode = 'wb', 754 encoding = encoding, 755 errors = 'strict' 756 ) 757 758 file.write(template % (u'013', u'8000', u'6301')) 759 file.write(template % (u'013', u'9218', u'2.10')) 760 if external_id_type is None: 761 file.write(template % (u'%03d' % (9 + len(str(self.ID))), u'3000', self.ID)) 762 else: 763 ext_ids = self.get_external_ids(id_type = external_id_type) 764 if len(ext_ids) > 0: 765 file.write(template % (u'%03d' % (9 + len(ext_ids[0]['value'])), u'3000', ext_ids[0]['value'])) 766 file.write(template % (u'%03d' % (9 + len(self._payload[self._idx['lastnames']])), u'3101', self._payload[self._idx['lastnames']])) 767 file.write(template % (u'%03d' % (9 + len(self._payload[self._idx['firstnames']])), u'3102', self._payload[self._idx['firstnames']])) 768 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'))) 769 file.write(template % (u'010', u'3110', gmXdtMappings.map_gender_gm2xdt[self._payload[self._idx['gender']]])) 770 file.write(template % (u'025', u'6330', 'GNUmed::9206::encoding')) 771 file.write(template % (u'%03d' % (9 + len(encoding)), u'6331', encoding)) 772 if external_id_type is None: 773 file.write(template % (u'029', u'6332', u'GNUmed::3000::source')) 774 file.write(template % (u'017', u'6333', u'internal')) 775 else: 776 if len(ext_ids) > 0: 777 file.write(template % (u'029', u'6332', u'GNUmed::3000::source')) 778 file.write(template % (u'%03d' % (9 + len(external_id_type)), u'6333', external_id_type)) 779 780 file.close()
781 #-------------------------------------------------------- 782 # occupations API 783 #--------------------------------------------------------
784 - def get_occupations(self):
785 return gmDemographicRecord.get_occupations(pk_identity = self.pk_obj)
786 #-------------------------------------------------------- 823 #-------------------------------------------------------- 831 #-------------------------------------------------------- 832 # comms API 833 #--------------------------------------------------------
834 - def get_comm_channels(self, comm_medium=None):
835 cmd = u"select * from dem.v_person_comms where pk_identity = %s" 836 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}], get_col_idx = True) 837 838 filtered = rows 839 840 if comm_medium is not None: 841 filtered = [] 842 for row in rows: 843 if row['comm_type'] == comm_medium: 844 filtered.append(row) 845 846 return [ gmDemographicRecord.cCommChannel(row = { 847 'pk_field': 'pk_lnk_identity2comm', 848 'data': r, 849 'idx': idx 850 }) for r in filtered 851 ]
852 #-------------------------------------------------------- 870 #-------------------------------------------------------- 876 #-------------------------------------------------------- 877 # contacts API 878 #--------------------------------------------------------
879 - def get_addresses(self, address_type=None):
880 cmd = u"select * from dem.v_pat_addresses where pk_identity=%s" 881 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}], get_col_idx=True) 882 addresses = [] 883 for r in rows: 884 addresses.append(gmDemographicRecord.cPatientAddress(row={'idx': idx, 'data': r, 'pk_field': 'pk_address'})) 885 886 filtered = addresses 887 888 if address_type is not None: 889 filtered = [] 890 for adr in addresses: 891 if adr['address_type'] == address_type: 892 filtered.append(adr) 893 894 return filtered
895 #-------------------------------------------------------- 946 #---------------------------------------------------------------------- 959 #---------------------------------------------------------------------- 960 # relatives API 961 #----------------------------------------------------------------------
962 - def get_relatives(self):
963 cmd = u""" 964 select 965 t.description, 966 vbp.pk_identity as id, 967 title, 968 firstnames, 969 lastnames, 970 dob, 971 cob, 972 gender, 973 karyotype, 974 pupic, 975 pk_marital_status, 976 marital_status, 977 xmin_identity, 978 preferred 979 from 980 dem.v_basic_person vbp, dem.relation_types t, dem.lnk_person2relative l 981 where 982 ( 983 l.id_identity = %(pk)s and 984 vbp.pk_identity = l.id_relative and 985 t.id = l.id_relation_type 986 ) or ( 987 l.id_relative = %(pk)s and 988 vbp.pk_identity = l.id_identity and 989 t.inverse = l.id_relation_type 990 )""" 991 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj}}]) 992 if len(rows) == 0: 993 return [] 994 return [(row[0], cIdentity(row = {'data': row[1:], 'idx':idx, 'pk_field': 'pk'})) for row in rows]
995 #-------------------------------------------------------- 1015 #----------------------------------------------------------------------
1016 - def delete_relative(self, relation):
1017 # unlink only, don't delete relative itself 1018 self.set_relative(None, relation)
1019 #--------------------------------------------------------
1021 if self._payload[self._idx['pk_emergency_contact']] is None: 1022 return None 1023 return cIdentity(aPK_obj = self._payload[self._idx['pk_emergency_contact']])
1024 1025 emergency_contact_in_database = property(_get_emergency_contact_from_database, lambda x:x) 1026 #---------------------------------------------------------------------- 1027 # age/dob related 1028 #----------------------------------------------------------------------
1029 - def get_medical_age(self):
1030 dob = self['dob'] 1031 1032 if dob is None: 1033 return u'??' 1034 1035 if dob > gmDateTime.pydt_now_here(): 1036 return _('invalid age: DOB in the future') 1037 1038 death = self['deceased'] 1039 1040 if death is None: 1041 return gmDateTime.format_apparent_age_medically ( 1042 age = gmDateTime.calculate_apparent_age(start = dob) 1043 ) 1044 1045 if dob > death: 1046 return _('invalid age: DOB after death') 1047 1048 return u'%s%s' % ( 1049 gmTools.u_latin_cross, 1050 gmDateTime.format_apparent_age_medically ( 1051 age = gmDateTime.calculate_apparent_age ( 1052 start = dob, 1053 end = self['deceased'] 1054 ) 1055 ) 1056 )
1057 #----------------------------------------------------------------------
1058 - def dob_in_range(self, min_distance=u'1 week', max_distance=u'1 week'):
1059 cmd = u'select dem.dob_is_in_range(%(dob)s, %(min)s, %(max)s)' 1060 rows, idx = gmPG2.run_ro_queries ( 1061 queries = [{ 1062 'cmd': cmd, 1063 'args': {'dob': self['dob'], 'min': min_distance, 'max': max_distance} 1064 }] 1065 ) 1066 return rows[0][0]
1067 #---------------------------------------------------------------------- 1068 # practice related 1069 #----------------------------------------------------------------------
1070 - def get_last_encounter(self):
1071 cmd = u'select * from clin.v_most_recent_encounters where pk_patient=%s' 1072 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self._payload[self._idx['pk_identity']]]}]) 1073 if len(rows) > 0: 1074 return rows[0] 1075 else: 1076 return None
1077 #--------------------------------------------------------
1078 - def _get_messages(self):
1079 return gmProviderInbox.get_inbox_messages(pk_patient = self._payload[self._idx['pk_identity']])
1080
1081 - def _set_messages(self, messages):
1082 return
1083 1084 messages = property(_get_messages, _set_messages) 1085 #--------------------------------------------------------
1086 - def delete_message(self, pk=None):
1087 return gmProviderInbox.delete_inbox_message(inbox_message = pk)
1088 #--------------------------------------------------------
1089 - def _get_primary_provider(self):
1090 if self._payload[self._idx['pk_primary_provider']] is None: 1091 return None 1092 from Gnumed.business import gmStaff 1093 return gmStaff.cStaff(aPK_obj = self._payload[self._idx['pk_primary_provider']])
1094 1095 primary_provider = property(_get_primary_provider, lambda x:x) 1096 #---------------------------------------------------------------------- 1097 # convenience 1098 #----------------------------------------------------------------------
1099 - def get_dirname(self):
1100 """Format patient demographics into patient specific path name fragment.""" 1101 return '%s-%s%s-%s' % ( 1102 self._payload[self._idx['lastnames']].replace(u' ', u'_'), 1103 self._payload[self._idx['firstnames']].replace(u' ', u'_'), 1104 gmTools.coalesce(self._payload[self._idx['preferred']], u'', template_initial = u'-(%s)'), 1105 self.get_formatted_dob(format = '%Y-%m-%d', encoding = gmI18N.get_encoding()) 1106 )
1107 #============================================================
1108 -class cPatient(cIdentity):
1109 """Represents a person which is a patient. 1110 1111 - a specializing subclass of cIdentity turning it into a patient 1112 - its use is to cache subobjects like EMR and document folder 1113 """
1114 - def __init__(self, aPK_obj=None, row=None):
1115 cIdentity.__init__(self, aPK_obj=aPK_obj, row=row) 1116 self.__db_cache = {} 1117 self.__emr_access_lock = threading.Lock()
1118 #--------------------------------------------------------
1119 - def cleanup(self):
1120 """Do cleanups before dying. 1121 1122 - note that this may be called in a thread 1123 """ 1124 if self.__db_cache.has_key('clinical record'): 1125 self.__db_cache['clinical record'].cleanup() 1126 if self.__db_cache.has_key('document folder'): 1127 self.__db_cache['document folder'].cleanup() 1128 cIdentity.cleanup(self)
1129 #----------------------------------------------------------
1130 - def get_emr(self):
1131 # attempt = 1 1132 # got_lock = self.__emr_access_lock.acquire(False) 1133 # while not got_lock: 1134 # if attempt == 100: # 100 x 500ms -> 50 seconds timeout 1135 # raise AttributeError('cannot access EMR') 1136 # attempt += 1 1137 # time.sleep(0.5) # 500ms 1138 # got_lock = self.__emr_access_lock.acquire(False) 1139 if not self.__emr_access_lock.acquire(False): 1140 raise AttributeError('cannot access EMR') 1141 try: 1142 emr = self.__db_cache['clinical record'] 1143 self.__emr_access_lock.release() 1144 return emr 1145 except KeyError: 1146 pass 1147 1148 self.__db_cache['clinical record'] = gmClinicalRecord.cClinicalRecord(aPKey = self._payload[self._idx['pk_identity']]) 1149 self.__emr_access_lock.release() 1150 return self.__db_cache['clinical record']
1151 #--------------------------------------------------------
1152 - def get_document_folder(self):
1153 try: 1154 return self.__db_cache['document folder'] 1155 except KeyError: 1156 pass 1157 1158 self.__db_cache['document folder'] = cDocumentFolder(aPKey = self._payload[self._idx['pk_identity']]) 1159 return self.__db_cache['document folder']
1160 #============================================================
1161 -class gmCurrentPatient(gmBorg.cBorg):
1162 """Patient Borg to hold currently active patient. 1163 1164 There may be many instances of this but they all share state. 1165 """
1166 - def __init__(self, patient=None, forced_reload=False):
1167 """Change or get currently active patient. 1168 1169 patient: 1170 * None: get currently active patient 1171 * -1: unset currently active patient 1172 * cPatient instance: set active patient if possible 1173 """ 1174 # make sure we do have a patient pointer 1175 try: 1176 tmp = self.patient 1177 except AttributeError: 1178 self.patient = gmNull.cNull() 1179 self.__register_interests() 1180 # set initial lock state, 1181 # this lock protects against activating another patient 1182 # when we are controlled from a remote application 1183 self.__lock_depth = 0 1184 # initialize callback state 1185 self.__pre_selection_callbacks = [] 1186 1187 # user wants copy of current patient 1188 if patient is None: 1189 return None 1190 1191 # do nothing if patient is locked 1192 if self.locked: 1193 _log.error('patient [%s] is locked, cannot change to [%s]' % (self.patient['pk_identity'], patient)) 1194 return None 1195 1196 # user wants to explicitly unset current patient 1197 if patient == -1: 1198 _log.debug('explicitly unsetting current patient') 1199 if not self.__run_pre_selection_callbacks(): 1200 _log.debug('not unsetting current patient') 1201 return None 1202 self.__send_pre_selection_notification() 1203 self.patient.cleanup() 1204 self.patient = gmNull.cNull() 1205 self.__send_selection_notification() 1206 return None 1207 1208 # must be cPatient instance, then 1209 if not isinstance(patient, cPatient): 1210 _log.error('cannot set active patient to [%s], must be either None, -1 or cPatient instance' % str(patient)) 1211 raise TypeError, 'gmPerson.gmCurrentPatient.__init__(): <patient> must be None, -1 or cPatient instance but is: %s' % str(patient) 1212 1213 # same ID, no change needed 1214 if (self.patient['pk_identity'] == patient['pk_identity']) and not forced_reload: 1215 return None 1216 1217 # user wants different patient 1218 _log.debug('patient change [%s] -> [%s] requested', self.patient['pk_identity'], patient['pk_identity']) 1219 1220 # everything seems swell 1221 if not self.__run_pre_selection_callbacks(): 1222 _log.debug('not changing current patient') 1223 return None 1224 self.__send_pre_selection_notification() 1225 self.patient.cleanup() 1226 self.patient = patient 1227 self.patient.get_emr() 1228 self.__send_selection_notification() 1229 1230 return None
1231 #--------------------------------------------------------
1232 - def __register_interests(self):
1233 gmDispatcher.connect(signal = u'identity_mod_db', receiver = self._on_identity_change) 1234 gmDispatcher.connect(signal = u'name_mod_db', receiver = self._on_identity_change)
1235 #--------------------------------------------------------
1236 - def _on_identity_change(self):
1237 """Listen for patient *data* change.""" 1238 self.patient.refetch_payload()
1239 #-------------------------------------------------------- 1240 # external API 1241 #--------------------------------------------------------
1242 - def register_pre_selection_callback(self, callback=None):
1243 if not callable(callback): 1244 raise TypeError(u'callback [%s] not callable' % callback) 1245 1246 self.__pre_selection_callbacks.append(callback)
1247 #--------------------------------------------------------
1248 - def _get_connected(self):
1249 return (not isinstance(self.patient, gmNull.cNull))
1250
1251 - def _set_connected(self):
1252 raise AttributeError(u'invalid to set <connected> state')
1253 1254 connected = property(_get_connected, _set_connected) 1255 #--------------------------------------------------------
1256 - def _get_locked(self):
1257 return (self.__lock_depth > 0)
1258
1259 - def _set_locked(self, locked):
1260 if locked: 1261 self.__lock_depth = self.__lock_depth + 1 1262 gmDispatcher.send(signal='patient_locked') 1263 else: 1264 if self.__lock_depth == 0: 1265 _log.error('lock/unlock imbalance, trying to refcount lock depth below 0') 1266 return 1267 else: 1268 self.__lock_depth = self.__lock_depth - 1 1269 gmDispatcher.send(signal='patient_unlocked')
1270 1271 locked = property(_get_locked, _set_locked) 1272 #--------------------------------------------------------
1273 - def force_unlock(self):
1274 _log.info('forced patient unlock at lock depth [%s]' % self.__lock_depth) 1275 self.__lock_depth = 0 1276 gmDispatcher.send(signal='patient_unlocked')
1277 #-------------------------------------------------------- 1278 # patient change handling 1279 #--------------------------------------------------------
1281 if isinstance(self.patient, gmNull.cNull): 1282 return True 1283 1284 for call_back in self.__pre_selection_callbacks: 1285 try: 1286 successful = call_back() 1287 except: 1288 _log.exception('callback [%s] failed', call_back) 1289 print "*** pre-selection callback failed ***" 1290 print type(call_back) 1291 print call_back 1292 return False 1293 1294 if not successful: 1295 _log.debug('callback [%s] returned False', call_back) 1296 return False 1297 1298 return True
1299 #--------------------------------------------------------
1301 """Sends signal when another patient is about to become active. 1302 1303 This does NOT wait for signal handlers to complete. 1304 """ 1305 kwargs = { 1306 'signal': u'pre_patient_selection', 1307 'sender': id(self.__class__), 1308 'pk_identity': self.patient['pk_identity'] 1309 } 1310 gmDispatcher.send(**kwargs)
1311 #--------------------------------------------------------
1313 """Sends signal when another patient has actually been made active.""" 1314 kwargs = { 1315 'signal': u'post_patient_selection', 1316 'sender': id(self.__class__), 1317 'pk_identity': self.patient['pk_identity'] 1318 } 1319 gmDispatcher.send(**kwargs)
1320 #-------------------------------------------------------- 1321 # __getattr__ handling 1322 #--------------------------------------------------------
1323 - def __getattr__(self, attribute):
1324 if attribute == 'patient': 1325 raise AttributeError 1326 if not isinstance(self.patient, gmNull.cNull): 1327 return getattr(self.patient, attribute)
1328 #-------------------------------------------------------- 1329 # __get/setitem__ handling 1330 #--------------------------------------------------------
1331 - def __getitem__(self, attribute = None):
1332 """Return any attribute if known how to retrieve it by proxy. 1333 """ 1334 return self.patient[attribute]
1335 #--------------------------------------------------------
1336 - def __setitem__(self, attribute, value):
1337 self.patient[attribute] = value
1338 #============================================================ 1339 # match providers 1340 #============================================================
1341 -class cMatchProvider_Provider(gmMatchProvider.cMatchProvider_SQL2):
1342 - def __init__(self):
1343 gmMatchProvider.cMatchProvider_SQL2.__init__( 1344 self, 1345 queries = [ 1346 u"""SELECT 1347 pk_staff AS data, 1348 short_alias || ' (' || coalesce(title, '') || ' ' || firstnames || ' ' || lastnames || ')' AS list_label, 1349 short_alias || ' (' || coalesce(title, '') || ' ' || firstnames || ' ' || lastnames || ')' AS field_label 1350 FROM dem.v_staff 1351 WHERE 1352 is_active AND ( 1353 short_alias %(fragment_condition)s OR 1354 firstnames %(fragment_condition)s OR 1355 lastnames %(fragment_condition)s OR 1356 db_user %(fragment_condition)s 1357 ) 1358 """ 1359 ] 1360 ) 1361 self.setThresholds(1, 2, 3)
1362 #============================================================ 1363 # convenience functions 1364 #============================================================
1365 -def create_name(pk_person, firstnames, lastnames, active=False):
1366 queries = [{ 1367 'cmd': u"select dem.add_name(%s, %s, %s, %s)", 1368 'args': [pk_person, firstnames, lastnames, active] 1369 }] 1370 rows, idx = gmPG2.run_rw_queries(queries=queries, return_data=True) 1371 name = cPersonName(aPK_obj = rows[0][0]) 1372 return name
1373 #============================================================
1374 -def create_identity(gender=None, dob=None, lastnames=None, firstnames=None):
1375 1376 cmd1 = u"""INSERT INTO dem.identity (gender, dob) VALUES (%s, %s)""" 1377 cmd2 = u""" 1378 INSERT INTO dem.names ( 1379 id_identity, lastnames, firstnames 1380 ) VALUES ( 1381 currval('dem.identity_pk_seq'), coalesce(%s, 'xxxDEFAULTxxx'), coalesce(%s, 'xxxDEFAULTxxx') 1382 ) RETURNING id_identity""" 1383 rows, idx = gmPG2.run_rw_queries ( 1384 queries = [ 1385 {'cmd': cmd1, 'args': [gender, dob]}, 1386 {'cmd': cmd2, 'args': [lastnames, firstnames]} 1387 ], 1388 return_data = True 1389 ) 1390 ident = cIdentity(aPK_obj=rows[0][0]) 1391 gmHooks.run_hook_script(hook = u'post_person_creation') 1392 return ident
1393 #============================================================
1394 -def create_dummy_identity():
1395 cmd = u"INSERT INTO dem.identity(gender) VALUES ('xxxDEFAULTxxx') RETURNING pk" 1396 rows, idx = gmPG2.run_rw_queries ( 1397 queries = [{'cmd': cmd}], 1398 return_data = True 1399 ) 1400 return gmDemographicRecord.cIdentity(aPK_obj = rows[0][0])
1401 #============================================================
1402 -def set_active_patient(patient=None, forced_reload=False):
1403 """Set active patient. 1404 1405 If patient is -1 the active patient will be UNset. 1406 """ 1407 if isinstance(patient, cPatient): 1408 pat = patient 1409 elif isinstance(patient, cIdentity): 1410 pat = cPatient(aPK_obj=patient['pk_identity']) 1411 # elif isinstance(patient, cStaff): 1412 # pat = cPatient(aPK_obj=patient['pk_identity']) 1413 elif isinstance(patient, gmCurrentPatient): 1414 pat = patient.patient 1415 elif patient == -1: 1416 pat = patient 1417 else: 1418 raise ValueError('<patient> must be either -1, cPatient, cIdentity or gmCurrentPatient instance, is: %s' % patient) 1419 1420 # attempt to switch 1421 try: 1422 gmCurrentPatient(patient = pat, forced_reload = forced_reload) 1423 except: 1424 _log.exception('error changing active patient to [%s]' % patient) 1425 return False 1426 1427 return True
1428 #============================================================ 1429 # gender related 1430 #------------------------------------------------------------
1431 -def get_gender_list():
1432 """Retrieves the list of known genders from the database.""" 1433 global __gender_idx 1434 global __gender_list 1435 1436 if __gender_list is None: 1437 cmd = u"select tag, l10n_tag, label, l10n_label, sort_weight from dem.v_gender_labels order by sort_weight desc" 1438 __gender_list, __gender_idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True) 1439 1440 return (__gender_list, __gender_idx)
1441 #------------------------------------------------------------ 1442 map_gender2mf = { 1443 'm': u'm', 1444 'f': u'f', 1445 'tf': u'f', 1446 'tm': u'm', 1447 'h': u'mf' 1448 } 1449 #------------------------------------------------------------ 1450 # Maps GNUmed related i18n-aware gender specifiers to a unicode symbol. 1451 map_gender2symbol = { 1452 'm': u'\u2642', 1453 'f': u'\u2640', 1454 'tf': u'\u26A5\u2640', 1455 'tm': u'\u26A5\u2642', 1456 'h': u'\u26A5' 1457 # 'tf': u'\u2642\u2640-\u2640', 1458 # 'tm': u'\u2642\u2640-\u2642', 1459 # 'h': u'\u2642\u2640' 1460 } 1461 #------------------------------------------------------------
1462 -def map_gender2salutation(gender=None):
1463 """Maps GNUmed related i18n-aware gender specifiers to a human-readable salutation.""" 1464 1465 global __gender2salutation_map 1466 1467 if __gender2salutation_map is None: 1468 genders, idx = get_gender_list() 1469 __gender2salutation_map = { 1470 'm': _('Mr'), 1471 'f': _('Mrs'), 1472 'tf': u'', 1473 'tm': u'', 1474 'h': u'' 1475 } 1476 for g in genders: 1477 __gender2salutation_map[g[idx['l10n_tag']]] = __gender2salutation_map[g[idx['tag']]] 1478 __gender2salutation_map[g[idx['label']]] = __gender2salutation_map[g[idx['tag']]] 1479 __gender2salutation_map[g[idx['l10n_label']]] = __gender2salutation_map[g[idx['tag']]] 1480 1481 return __gender2salutation_map[gender]
1482 #------------------------------------------------------------
1483 -def map_firstnames2gender(firstnames=None):
1484 """Try getting the gender for the given first name.""" 1485 1486 if firstnames is None: 1487 return None 1488 1489 rows, idx = gmPG2.run_ro_queries(queries = [{ 1490 'cmd': u"select gender from dem.name_gender_map where name ilike %(fn)s limit 1", 1491 'args': {'fn': firstnames} 1492 }]) 1493 1494 if len(rows) == 0: 1495 return None 1496 1497 return rows[0][0]
1498 #============================================================
1499 -def get_persons_from_pks(pks=None):
1500 return [ cIdentity(aPK_obj = pk) for pk in pks ]
1501 #============================================================
1502 -def get_person_from_xdt(filename=None, encoding=None, dob_format=None):
1503 from Gnumed.business import gmXdtObjects 1504 return gmXdtObjects.read_person_from_xdt(filename=filename, encoding=encoding, dob_format=dob_format)
1505 #============================================================
1506 -def get_persons_from_pracsoft_file(filename=None, encoding='ascii'):
1507 from Gnumed.business import gmPracSoftAU 1508 return gmPracSoftAU.read_persons_from_pracsoft_file(filename=filename, encoding=encoding)
1509 #============================================================ 1510 # main/testing 1511 #============================================================ 1512 if __name__ == '__main__': 1513 1514 if len(sys.argv) == 1: 1515 sys.exit() 1516 1517 if sys.argv[1] != 'test': 1518 sys.exit() 1519 1520 import datetime 1521 1522 gmI18N.activate_locale() 1523 gmI18N.install_domain() 1524 gmDateTime.init() 1525 1526 #--------------------------------------------------------
1527 - def test_set_active_pat():
1528 1529 ident = cIdentity(1) 1530 print "setting active patient with", ident 1531 set_active_patient(patient=ident) 1532 1533 patient = cPatient(12) 1534 print "setting active patient with", patient 1535 set_active_patient(patient=patient) 1536 1537 pat = gmCurrentPatient() 1538 print pat['dob'] 1539 #pat['dob'] = 'test' 1540 1541 # staff = cStaff() 1542 # print "setting active patient with", staff 1543 # set_active_patient(patient=staff) 1544 1545 print "setting active patient with -1" 1546 set_active_patient(patient=-1)
1547 #--------------------------------------------------------
1548 - def test_dto_person():
1549 dto = cDTO_person() 1550 dto.firstnames = 'Sepp' 1551 dto.lastnames = 'Herberger' 1552 dto.gender = 'male' 1553 dto.dob = pyDT.datetime.now(tz=gmDateTime.gmCurrentLocalTimezone) 1554 print dto 1555 1556 print dto['firstnames'] 1557 print dto['lastnames'] 1558 print dto['gender'] 1559 print dto['dob'] 1560 1561 for key in dto.keys(): 1562 print key
1563 #--------------------------------------------------------
1564 - def test_identity():
1565 # create patient 1566 print '\n\nCreating identity...' 1567 new_identity = create_identity(gender='m', dob='2005-01-01', lastnames='test lastnames', firstnames='test firstnames') 1568 print 'Identity created: %s' % new_identity 1569 1570 print '\nSetting title and gender...' 1571 new_identity['title'] = 'test title'; 1572 new_identity['gender'] = 'f'; 1573 new_identity.save_payload() 1574 print 'Refetching identity from db: %s' % cIdentity(aPK_obj=new_identity['pk_identity']) 1575 1576 print '\nGetting all names...' 1577 for a_name in new_identity.get_names(): 1578 print a_name 1579 print 'Active name: %s' % (new_identity.get_active_name()) 1580 print 'Setting nickname...' 1581 new_identity.set_nickname(nickname='test nickname') 1582 print 'Refetching all names...' 1583 for a_name in new_identity.get_names(): 1584 print a_name 1585 print 'Active name: %s' % (new_identity.get_active_name()) 1586 1587 print '\nIdentity occupations: %s' % new_identity['occupations'] 1588 print 'Creating identity occupation...' 1589 new_identity.link_occupation('test occupation') 1590 print 'Identity occupations: %s' % new_identity['occupations'] 1591 1592 print '\nIdentity addresses: %s' % new_identity.get_addresses() 1593 print 'Creating identity address...' 1594 # make sure the state exists in the backend 1595 new_identity.link_address ( 1596 number = 'test 1234', 1597 street = 'test street', 1598 postcode = 'test postcode', 1599 urb = 'test urb', 1600 state = 'SN', 1601 country = 'DE' 1602 ) 1603 print 'Identity addresses: %s' % new_identity.get_addresses() 1604 1605 print '\nIdentity communications: %s' % new_identity.get_comm_channels() 1606 print 'Creating identity communication...' 1607 new_identity.link_comm_channel('homephone', '1234566') 1608 print 'Identity communications: %s' % new_identity.get_comm_channels()
1609 #--------------------------------------------------------
1610 - def test_name():
1611 for pk in range(1,16): 1612 name = cPersonName(aPK_obj=pk) 1613 print name.description 1614 print ' ', name
1615 #-------------------------------------------------------- 1616 #test_dto_person() 1617 #test_identity() 1618 #test_set_active_pat() 1619 #test_search_by_dto() 1620 test_name() 1621 1622 #map_gender2salutation('m') 1623 # module functions 1624 #genders, idx = get_gender_list() 1625 #print "\n\nRetrieving gender enum (tag, label, weight):" 1626 #for gender in genders: 1627 # print "%s, %s, %s" % (gender[idx['tag']], gender[idx['l10n_label']], gender[idx['sort_weight']]) 1628 1629 #comms = get_comm_list() 1630 #print "\n\nRetrieving communication media enum (id, description): %s" % comms 1631 1632 #============================================================ 1633