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  __gender2string_map = None 
  41   
  42  #============================================================ 
  43  # FIXME: make this work as a mapping type, too 
44 -class cDTO_person(object):
45
46 - def __init__(self):
47 self.identity = None 48 self.external_ids = [] 49 self.comm_channels = [] 50 self.addresses = []
51 #-------------------------------------------------------- 52 # external API 53 #--------------------------------------------------------
54 - def keys(self):
55 return 'firstnames lastnames dob gender'.split()
56 #--------------------------------------------------------
57 - def delete_from_source(self):
58 pass
59 #--------------------------------------------------------
60 - def get_candidate_identities(self, can_create=False):
61 """Generate generic queries. 62 63 - not locale dependant 64 - data -> firstnames, lastnames, dob, gender 65 66 shall we mogrify name parts ? probably not as external 67 sources should know what they do 68 69 finds by inactive name, too, but then shows 70 the corresponding active name ;-) 71 72 Returns list of matching identities (may be empty) 73 or None if it was told to create an identity but couldn't. 74 """ 75 where_snippets = [] 76 args = {} 77 78 where_snippets.append(u'firstnames = %(first)s') 79 args['first'] = self.firstnames 80 81 where_snippets.append(u'lastnames = %(last)s') 82 args['last'] = self.lastnames 83 84 if self.dob is not None: 85 where_snippets.append(u"dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %(dob)s)") 86 args['dob'] = self.dob.replace(hour = 23, minute = 59, second = 59) 87 88 if self.gender is not None: 89 where_snippets.append('gender = %(sex)s') 90 args['sex'] = self.gender 91 92 cmd = u""" 93 SELECT *, '%s' AS match_type 94 FROM dem.v_basic_person 95 WHERE 96 pk_identity IN ( 97 SELECT pk_identity FROM dem.v_person_names WHERE %s 98 ) 99 ORDER BY lastnames, firstnames, dob""" % ( 100 _('external patient source (name, gender, date of birth)'), 101 ' AND '.join(where_snippets) 102 ) 103 104 try: 105 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx=True) 106 except: 107 _log.error(u'cannot get candidate identities for dto "%s"' % self) 108 _log.exception('query %s' % cmd) 109 rows = [] 110 111 if len(rows) == 0: 112 _log.debug('no candidate identity matches found') 113 if not can_create: 114 return [] 115 ident = self.import_into_database() 116 if ident is None: 117 return None 118 identities = [ident] 119 else: 120 identities = [ cIdentity(row = {'pk_field': 'pk_identity', 'data': row, 'idx': idx}) for row in rows ] 121 122 return identities
123 #--------------------------------------------------------
124 - def import_into_database(self):
125 """Imports self into the database.""" 126 127 self.identity = create_identity ( 128 firstnames = self.firstnames, 129 lastnames = self.lastnames, 130 gender = self.gender, 131 dob = self.dob 132 ) 133 134 if self.identity is None: 135 return None 136 137 for ext_id in self.external_ids: 138 try: 139 self.identity.add_external_id ( 140 type_name = ext_id['name'], 141 value = ext_id['value'], 142 issuer = ext_id['issuer'], 143 comment = ext_id['comment'] 144 ) 145 except StandardError: 146 _log.exception('cannot import <external ID> from external data source') 147 _log.log_stack_trace() 148 149 for comm in self.comm_channels: 150 try: 151 self.identity.link_comm_channel ( 152 comm_medium = comm['channel'], 153 url = comm['url'] 154 ) 155 except StandardError: 156 _log.exception('cannot import <comm channel> from external data source') 157 _log.log_stack_trace() 158 159 for adr in self.addresses: 160 try: 161 self.identity.link_address ( 162 number = adr['number'], 163 street = adr['street'], 164 postcode = adr['zip'], 165 urb = adr['urb'], 166 state = adr['region'], 167 country = adr['country'] 168 ) 169 except StandardError: 170 _log.exception('cannot import <address> from external data source') 171 _log.log_stack_trace() 172 173 return self.identity
174 #--------------------------------------------------------
175 - def import_extra_data(self, *args, **kwargs):
176 pass
177 #--------------------------------------------------------
178 - def remember_external_id(self, name=None, value=None, issuer=None, comment=None):
179 value = value.strip() 180 if value == u'': 181 return 182 name = name.strip() 183 if name == u'': 184 raise ValueError(_('<name> cannot be empty')) 185 issuer = issuer.strip() 186 if issuer == u'': 187 raise ValueError(_('<issuer> cannot be empty')) 188 self.external_ids.append({'name': name, 'value': value, 'issuer': issuer, 'comment': comment})
189 #--------------------------------------------------------
190 - def remember_comm_channel(self, channel=None, url=None):
191 url = url.strip() 192 if url == u'': 193 return 194 channel = channel.strip() 195 if channel == u'': 196 raise ValueError(_('<channel> cannot be empty')) 197 self.comm_channels.append({'channel': channel, 'url': url})
198 #--------------------------------------------------------
199 - def remember_address(self, number=None, street=None, urb=None, region=None, zip=None, country=None):
200 number = number.strip() 201 if number == u'': 202 raise ValueError(_('<number> cannot be empty')) 203 street = street.strip() 204 if street == u'': 205 raise ValueError(_('<street> cannot be empty')) 206 urb = urb.strip() 207 if urb == u'': 208 raise ValueError(_('<urb> cannot be empty')) 209 zip = zip.strip() 210 if zip == u'': 211 raise ValueError(_('<zip> cannot be empty')) 212 country = country.strip() 213 if country == u'': 214 raise ValueError(_('<country> cannot be empty')) 215 region = region.strip() 216 if region == u'': 217 region = u'??' 218 self.addresses.append ({ 219 u'number': number, 220 u'street': street, 221 u'zip': zip, 222 u'urb': urb, 223 u'region': region, 224 u'country': country 225 })
226 #-------------------------------------------------------- 227 # customizing behaviour 228 #--------------------------------------------------------
229 - def __str__(self):
230 return u'<%s @ %s: %s %s (%s) %s>' % ( 231 self.__class__.__name__, 232 id(self), 233 self.firstnames, 234 self.lastnames, 235 self.gender, 236 self.dob 237 )
238 #--------------------------------------------------------
239 - def __setattr__(self, attr, val):
240 """Do some sanity checks on self.* access.""" 241 242 if attr == 'gender': 243 glist, idx = get_gender_list() 244 for gender in glist: 245 if str(val) in [gender[0], gender[1], gender[2], gender[3]]: 246 val = gender[idx['tag']] 247 object.__setattr__(self, attr, val) 248 return 249 raise ValueError('invalid gender: [%s]' % val) 250 251 if attr == 'dob': 252 if val is not None: 253 if not isinstance(val, pyDT.datetime): 254 raise TypeError('invalid type for DOB (must be datetime.datetime): %s [%s]' % (type(val), val)) 255 if val.tzinfo is None: 256 raise ValueError('datetime.datetime instance is lacking a time zone: [%s]' % val.isoformat()) 257 258 object.__setattr__(self, attr, val) 259 return
260 #--------------------------------------------------------
261 - def __getitem__(self, attr):
262 return getattr(self, attr)
263 #============================================================
264 -class cPersonName(gmBusinessDBObject.cBusinessDBObject):
265 _cmd_fetch_payload = u"SELECT * FROM dem.v_person_names WHERE pk_name = %s" 266 _cmds_store_payload = [ 267 u"""UPDATE dem.names SET 268 active = FALSE 269 WHERE 270 %(active_name)s IS TRUE -- act only when needed and only 271 AND 272 id_identity = %(pk_identity)s -- on names of this identity 273 AND 274 active IS TRUE -- which are active 275 AND 276 id != %(pk_name)s -- but NOT *this* name 277 """, 278 u"""update dem.names set 279 active = %(active_name)s, 280 preferred = %(preferred)s, 281 comment = %(comment)s 282 where 283 id = %(pk_name)s and 284 id_identity = %(pk_identity)s and -- belt and suspenders 285 xmin = %(xmin_name)s""", 286 u"""select xmin as xmin_name from dem.names where id = %(pk_name)s""" 287 ] 288 _updatable_fields = ['active_name', 'preferred', 'comment'] 289 #--------------------------------------------------------
290 - def __setitem__(self, attribute, value):
291 if attribute == 'active_name': 292 # cannot *directly* deactivate a name, only indirectly 293 # by activating another one 294 # FIXME: should be done at DB level 295 if self._payload[self._idx['active_name']] is True: 296 return 297 gmBusinessDBObject.cBusinessDBObject.__setitem__(self, attribute, value)
298 #--------------------------------------------------------
299 - def _get_description(self):
300 return '%(last)s, %(title)s %(first)s%(nick)s' % { 301 'last': self._payload[self._idx['lastnames']], 302 'title': gmTools.coalesce ( 303 self._payload[self._idx['title']], 304 map_gender2salutation(self._payload[self._idx['gender']]) 305 ), 306 'first': self._payload[self._idx['firstnames']], 307 'nick': gmTools.coalesce(self._payload[self._idx['preferred']], u'', u" '%s'", u'%s') 308 }
309 310 description = property(_get_description, lambda x:x)
311 #============================================================
312 -class cIdentity(gmBusinessDBObject.cBusinessDBObject):
313 _cmd_fetch_payload = u"SELECT * FROM dem.v_basic_person WHERE pk_identity = %s" 314 _cmds_store_payload = [ 315 u"""UPDATE dem.identity SET 316 gender = %(gender)s, 317 dob = %(dob)s, 318 dob_is_estimated = %(dob_is_estimated)s, 319 tob = %(tob)s, 320 cob = gm.nullify_empty_string(%(cob)s), 321 title = gm.nullify_empty_string(%(title)s), 322 fk_marital_status = %(pk_marital_status)s, 323 karyotype = gm.nullify_empty_string(%(karyotype)s), 324 pupic = gm.nullify_empty_string(%(pupic)s), 325 deceased = %(deceased)s, 326 emergency_contact = gm.nullify_empty_string(%(emergency_contact)s), 327 fk_emergency_contact = %(pk_emergency_contact)s, 328 fk_primary_provider = %(pk_primary_provider)s, 329 comment = gm.nullify_empty_string(%(comment)s) 330 WHERE 331 pk = %(pk_identity)s and 332 xmin = %(xmin_identity)s 333 RETURNING 334 xmin AS xmin_identity""" 335 ] 336 _updatable_fields = [ 337 "title", 338 "dob", 339 "tob", 340 "cob", 341 "gender", 342 "pk_marital_status", 343 "karyotype", 344 "pupic", 345 'deceased', 346 'emergency_contact', 347 'pk_emergency_contact', 348 'pk_primary_provider', 349 'comment', 350 'dob_is_estimated' 351 ] 352 #--------------------------------------------------------
353 - def _get_ID(self):
354 return self._payload[self._idx['pk_identity']]
355 - def _set_ID(self, value):
356 raise AttributeError('setting ID of identity is not allowed')
357 ID = property(_get_ID, _set_ID) 358 #--------------------------------------------------------
359 - def __setitem__(self, attribute, value):
360 361 if attribute == 'dob': 362 if value is not None: 363 364 if isinstance(value, pyDT.datetime): 365 if value.tzinfo is None: 366 raise ValueError('datetime.datetime instance is lacking a time zone: [%s]' % dt.isoformat()) 367 else: 368 raise TypeError, '[%s]: type [%s] (%s) invalid for attribute [dob], must be datetime.datetime or None' % (self.__class__.__name__, type(value), value) 369 370 # compare DOB at seconds level 371 if self._payload[self._idx['dob']] is not None: 372 old_dob = gmDateTime.pydt_strftime ( 373 self._payload[self._idx['dob']], 374 format = '%Y %m %d %H %M %S', 375 accuracy = gmDateTime.acc_seconds 376 ) 377 new_dob = gmDateTime.pydt_strftime ( 378 value, 379 format = '%Y %m %d %H %M %S', 380 accuracy = gmDateTime.acc_seconds 381 ) 382 if new_dob == old_dob: 383 return 384 385 gmBusinessDBObject.cBusinessDBObject.__setitem__(self, attribute, value)
386 #--------------------------------------------------------
387 - def cleanup(self):
388 pass
389 #--------------------------------------------------------
390 - def _get_is_patient(self):
391 cmd = u""" 392 SELECT EXISTS ( 393 SELECT 1 394 FROM clin.v_emr_journal 395 WHERE 396 pk_patient = %(pat)s 397 AND 398 soap_cat IS NOT NULL 399 )""" 400 args = {'pat': self._payload[self._idx['pk_identity']]} 401 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 402 return rows[0][0]
403
404 - def _set_is_patient(self, value):
405 raise AttributeError('setting is_patient status of identity is not allowed')
406 407 is_patient = property(_get_is_patient, _set_is_patient) 408 #--------------------------------------------------------
409 - def _get_staff_id(self):
410 cmd = u"SELECT pk FROM dem.staff WHERE fk_identity = %(pk)s" 411 args = {'pk': self._payload[self._idx['pk_identity']]} 412 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 413 if len(rows) == 0: 414 return None 415 return rows[0][0]
416 417 staff_id = property(_get_staff_id, lambda x:x) 418 #-------------------------------------------------------- 419 # identity API 420 #--------------------------------------------------------
421 - def _get_gender_symbol(self):
422 return map_gender2symbol[self._payload[self._idx['gender']]]
423 424 gender_symbol = property(_get_gender_symbol, lambda x:x) 425 #--------------------------------------------------------
426 - def _get_gender_string(self):
427 return map_gender2string(gender = self._payload[self._idx['gender']])
428 429 gender_string = property(_get_gender_string, lambda x:x) 430 #--------------------------------------------------------
431 - def get_active_name(self):
432 names = self.get_names(active_only = True) 433 if len(names) == 0: 434 _log.error('cannot retrieve active name for patient [%s]', self._payload[self._idx['pk_identity']]) 435 return None 436 return names[0]
437 438 active_name = property(get_active_name, lambda x:x) 439 #--------------------------------------------------------
440 - def get_names(self, active_only=False, exclude_active=False):
441 442 args = {'pk_pat': self._payload[self._idx['pk_identity']]} 443 where_parts = [u'pk_identity = %(pk_pat)s'] 444 if active_only: 445 where_parts.append(u'active_name is True') 446 if exclude_active: 447 where_parts.append(u'active_name is False') 448 cmd = u""" 449 SELECT * 450 FROM dem.v_person_names 451 WHERE %s 452 ORDER BY active_name DESC, lastnames, firstnames 453 """ % u' AND '.join(where_parts) 454 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 455 456 if len(rows) == 0: 457 # no names registered for patient 458 return [] 459 460 names = [ cPersonName(row = {'idx': idx, 'data': r, 'pk_field': 'pk_name'}) for r in rows ] 461 return names
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 waiting_list_entries = property(get_waiting_list_entry, lambda x:x) 763 #--------------------------------------------------------
764 - def export_as_gdt(self, filename=None, encoding='iso-8859-15', external_id_type=None):
765 766 template = u'%s%s%s\r\n' 767 768 file = codecs.open ( 769 filename = filename, 770 mode = 'wb', 771 encoding = encoding, 772 errors = 'strict' 773 ) 774 775 file.write(template % (u'013', u'8000', u'6301')) 776 file.write(template % (u'013', u'9218', u'2.10')) 777 if external_id_type is None: 778 file.write(template % (u'%03d' % (9 + len(str(self.ID))), u'3000', self.ID)) 779 else: 780 ext_ids = self.get_external_ids(id_type = external_id_type) 781 if len(ext_ids) > 0: 782 file.write(template % (u'%03d' % (9 + len(ext_ids[0]['value'])), u'3000', ext_ids[0]['value'])) 783 file.write(template % (u'%03d' % (9 + len(self._payload[self._idx['lastnames']])), u'3101', self._payload[self._idx['lastnames']])) 784 file.write(template % (u'%03d' % (9 + len(self._payload[self._idx['firstnames']])), u'3102', self._payload[self._idx['firstnames']])) 785 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'))) 786 file.write(template % (u'010', u'3110', gmXdtMappings.map_gender_gm2xdt[self._payload[self._idx['gender']]])) 787 file.write(template % (u'025', u'6330', 'GNUmed::9206::encoding')) 788 file.write(template % (u'%03d' % (9 + len(encoding)), u'6331', encoding)) 789 if external_id_type is None: 790 file.write(template % (u'029', u'6332', u'GNUmed::3000::source')) 791 file.write(template % (u'017', u'6333', u'internal')) 792 else: 793 if len(ext_ids) > 0: 794 file.write(template % (u'029', u'6332', u'GNUmed::3000::source')) 795 file.write(template % (u'%03d' % (9 + len(external_id_type)), u'6333', external_id_type)) 796 797 file.close()
798 #-------------------------------------------------------- 799 # occupations API 800 #--------------------------------------------------------
801 - def get_occupations(self):
802 return gmDemographicRecord.get_occupations(pk_identity = self.pk_obj)
803 #-------------------------------------------------------- 840 #-------------------------------------------------------- 848 #-------------------------------------------------------- 849 # comms API 850 #--------------------------------------------------------
851 - def get_comm_channels(self, comm_medium=None):
852 cmd = u"select * from dem.v_person_comms where pk_identity = %s" 853 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}], get_col_idx = True) 854 855 filtered = rows 856 857 if comm_medium is not None: 858 filtered = [] 859 for row in rows: 860 if row['comm_type'] == comm_medium: 861 filtered.append(row) 862 863 return [ gmDemographicRecord.cCommChannel(row = { 864 'pk_field': 'pk_lnk_identity2comm', 865 'data': r, 866 'idx': idx 867 }) for r in filtered 868 ]
869 #-------------------------------------------------------- 887 #-------------------------------------------------------- 893 #-------------------------------------------------------- 894 # contacts API 895 #--------------------------------------------------------
896 - def get_addresses(self, address_type=None):
897 898 cmd = u"SELECT * FROM dem.v_pat_addresses WHERE pk_identity = %(pat)s" 899 args = {'pat': self.pk_obj} 900 if address_type is not None: 901 cmd = cmd + u" AND address_type = %(typ)s" 902 args['typ'] = address_type 903 904 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 905 906 return [ 907 gmDemographicRecord.cPatientAddress(row = {'idx': idx, 'data': r, 'pk_field': 'pk_address'}) 908 for r in rows 909 ]
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_formatted_dob(self, format='%x', encoding=None, none_string=None):
1045 return gmDateTime.format_dob ( 1046 self._payload[self._idx['dob']], 1047 format = format, 1048 encoding = encoding, 1049 none_string = none_string, 1050 dob_is_estimated = self._payload[self._idx['dob_is_estimated']] 1051 )
1052 #----------------------------------------------------------------------
1053 - def get_medical_age(self):
1054 dob = self['dob'] 1055 1056 if dob is None: 1057 return u'??' 1058 1059 if dob > gmDateTime.pydt_now_here(): 1060 return _('invalid age: DOB in the future') 1061 1062 death = self['deceased'] 1063 1064 if death is None: 1065 return u'%s%s' % ( 1066 gmTools.bool2subst ( 1067 self._payload[self._idx['dob_is_estimated']], 1068 gmTools.u_almost_equal_to, 1069 u'' 1070 ), 1071 gmDateTime.format_apparent_age_medically ( 1072 age = gmDateTime.calculate_apparent_age(start = dob) 1073 ) 1074 ) 1075 1076 if dob > death: 1077 return _('invalid age: DOB after death') 1078 1079 return u'%s%s%s' % ( 1080 gmTools.u_latin_cross, 1081 gmTools.bool2subst ( 1082 self._payload[self._idx['dob_is_estimated']], 1083 gmTools.u_almost_equal_to, 1084 u'' 1085 ), 1086 gmDateTime.format_apparent_age_medically ( 1087 age = gmDateTime.calculate_apparent_age ( 1088 start = dob, 1089 end = self['deceased'] 1090 ) 1091 ) 1092 )
1093 #----------------------------------------------------------------------
1094 - def dob_in_range(self, min_distance=u'1 week', max_distance=u'1 week'):
1095 cmd = u'select dem.dob_is_in_range(%(dob)s, %(min)s, %(max)s)' 1096 rows, idx = gmPG2.run_ro_queries ( 1097 queries = [{ 1098 'cmd': cmd, 1099 'args': {'dob': self['dob'], 'min': min_distance, 'max': max_distance} 1100 }] 1101 ) 1102 return rows[0][0]
1103 #---------------------------------------------------------------------- 1104 # practice related 1105 #----------------------------------------------------------------------
1106 - def get_last_encounter(self):
1107 cmd = u'select * from clin.v_most_recent_encounters where pk_patient=%s' 1108 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self._payload[self._idx['pk_identity']]]}]) 1109 if len(rows) > 0: 1110 return rows[0] 1111 else: 1112 return None
1113 #--------------------------------------------------------
1114 - def _get_messages(self):
1115 return gmProviderInbox.get_inbox_messages(pk_patient = self._payload[self._idx['pk_identity']])
1116 1117 messages = property(_get_messages, lambda x:x) 1118 #--------------------------------------------------------
1119 - def _get_due_messages(self):
1120 return gmProviderInbox.get_due_messages(pk_patient = self._payload[self._idx['pk_identity']])
1121 1122 due_messages = property(_get_due_messages, lambda x:x) 1123 #--------------------------------------------------------
1124 - def delete_message(self, pk=None):
1125 return gmProviderInbox.delete_inbox_message(inbox_message = pk)
1126 #--------------------------------------------------------
1127 - def _get_primary_provider(self):
1128 if self._payload[self._idx['pk_primary_provider']] is None: 1129 return None 1130 from Gnumed.business import gmStaff 1131 return gmStaff.cStaff(aPK_obj = self._payload[self._idx['pk_primary_provider']])
1132 1133 primary_provider = property(_get_primary_provider, lambda x:x) 1134 #---------------------------------------------------------------------- 1135 # convenience 1136 #----------------------------------------------------------------------
1137 - def get_dirname(self):
1138 """Format patient demographics into patient specific path name fragment.""" 1139 return '%s-%s%s-%s' % ( 1140 self._payload[self._idx['lastnames']].replace(u' ', u'_'), 1141 self._payload[self._idx['firstnames']].replace(u' ', u'_'), 1142 gmTools.coalesce(self._payload[self._idx['preferred']], u'', template_initial = u'-(%s)'), 1143 self.get_formatted_dob(format = '%Y-%m-%d', encoding = gmI18N.get_encoding()) 1144 )
1145 #============================================================
1146 -class cPatient(cIdentity):
1147 """Represents a person which is a patient. 1148 1149 - a specializing subclass of cIdentity turning it into a patient 1150 - its use is to cache subobjects like EMR and document folder 1151 """
1152 - def __init__(self, aPK_obj=None, row=None):
1153 cIdentity.__init__(self, aPK_obj=aPK_obj, row=row) 1154 self.__db_cache = {} 1155 self.__emr_access_lock = threading.Lock()
1156 #--------------------------------------------------------
1157 - def cleanup(self):
1158 """Do cleanups before dying. 1159 1160 - note that this may be called in a thread 1161 """ 1162 if self.__db_cache.has_key('clinical record'): 1163 self.__db_cache['clinical record'].cleanup() 1164 if self.__db_cache.has_key('document folder'): 1165 self.__db_cache['document folder'].cleanup() 1166 cIdentity.cleanup(self)
1167 #----------------------------------------------------------
1168 - def get_emr(self):
1169 # attempt = 1 1170 # got_lock = self.__emr_access_lock.acquire(False) 1171 # while not got_lock: 1172 # if attempt == 100: # 100 x 500ms -> 50 seconds timeout 1173 # raise AttributeError('cannot access EMR') 1174 # attempt += 1 1175 # time.sleep(0.5) # 500ms 1176 # got_lock = self.__emr_access_lock.acquire(False) 1177 if not self.__emr_access_lock.acquire(False): 1178 raise AttributeError('cannot access EMR') 1179 try: 1180 emr = self.__db_cache['clinical record'] 1181 self.__emr_access_lock.release() 1182 return emr 1183 except KeyError: 1184 pass 1185 1186 self.__db_cache['clinical record'] = gmClinicalRecord.cClinicalRecord(aPKey = self._payload[self._idx['pk_identity']]) 1187 self.__emr_access_lock.release() 1188 return self.__db_cache['clinical record']
1189 1190 emr = property(get_emr, lambda x:x) 1191 #--------------------------------------------------------
1192 - def get_document_folder(self):
1193 try: 1194 return self.__db_cache['document folder'] 1195 except KeyError: 1196 pass 1197 1198 self.__db_cache['document folder'] = cDocumentFolder(aPKey = self._payload[self._idx['pk_identity']]) 1199 return self.__db_cache['document folder']
1200 1201 document_folder = property(get_document_folder, lambda x:x)
1202 #============================================================
1203 -class gmCurrentPatient(gmBorg.cBorg):
1204 """Patient Borg to hold currently active patient. 1205 1206 There may be many instances of this but they all share state. 1207 """
1208 - def __init__(self, patient=None, forced_reload=False):
1209 """Change or get currently active patient. 1210 1211 patient: 1212 * None: get currently active patient 1213 * -1: unset currently active patient 1214 * cPatient instance: set active patient if possible 1215 """ 1216 # make sure we do have a patient pointer 1217 try: 1218 tmp = self.patient 1219 except AttributeError: 1220 self.patient = gmNull.cNull() 1221 self.__register_interests() 1222 # set initial lock state, 1223 # this lock protects against activating another patient 1224 # when we are controlled from a remote application 1225 self.__lock_depth = 0 1226 # initialize callback state 1227 self.__pre_selection_callbacks = [] 1228 1229 # user wants copy of current patient 1230 if patient is None: 1231 return None 1232 1233 # do nothing if patient is locked 1234 if self.locked: 1235 _log.error('patient [%s] is locked, cannot change to [%s]' % (self.patient['pk_identity'], patient)) 1236 return None 1237 1238 # user wants to explicitly unset current patient 1239 if patient == -1: 1240 _log.debug('explicitly unsetting current patient') 1241 if not self.__run_pre_selection_callbacks(): 1242 _log.debug('not unsetting current patient') 1243 return None 1244 self.__send_pre_selection_notification() 1245 self.patient.cleanup() 1246 self.patient = gmNull.cNull() 1247 self.__send_selection_notification() 1248 return None 1249 1250 # must be cPatient instance, then 1251 if not isinstance(patient, cPatient): 1252 _log.error('cannot set active patient to [%s], must be either None, -1 or cPatient instance' % str(patient)) 1253 raise TypeError, 'gmPerson.gmCurrentPatient.__init__(): <patient> must be None, -1 or cPatient instance but is: %s' % str(patient) 1254 1255 # same ID, no change needed 1256 if (self.patient['pk_identity'] == patient['pk_identity']) and not forced_reload: 1257 return None 1258 1259 # user wants different patient 1260 _log.debug('patient change [%s] -> [%s] requested', self.patient['pk_identity'], patient['pk_identity']) 1261 1262 # everything seems swell 1263 if not self.__run_pre_selection_callbacks(): 1264 _log.debug('not changing current patient') 1265 return None 1266 self.__send_pre_selection_notification() 1267 self.patient.cleanup() 1268 self.patient = patient 1269 self.patient.get_emr() 1270 self.__send_selection_notification() 1271 1272 return None
1273 #--------------------------------------------------------
1274 - def __register_interests(self):
1275 gmDispatcher.connect(signal = u'identity_mod_db', receiver = self._on_identity_change) 1276 gmDispatcher.connect(signal = u'name_mod_db', receiver = self._on_identity_change)
1277 #--------------------------------------------------------
1278 - def _on_identity_change(self):
1279 """Listen for patient *data* change.""" 1280 self.patient.refetch_payload()
1281 #-------------------------------------------------------- 1282 # external API 1283 #--------------------------------------------------------
1284 - def register_pre_selection_callback(self, callback=None):
1285 if not callable(callback): 1286 raise TypeError(u'callback [%s] not callable' % callback) 1287 1288 self.__pre_selection_callbacks.append(callback)
1289 #--------------------------------------------------------
1290 - def _get_connected(self):
1291 return (not isinstance(self.patient, gmNull.cNull))
1292
1293 - def _set_connected(self):
1294 raise AttributeError(u'invalid to set <connected> state')
1295 1296 connected = property(_get_connected, _set_connected) 1297 #--------------------------------------------------------
1298 - def _get_locked(self):
1299 return (self.__lock_depth > 0)
1300
1301 - def _set_locked(self, locked):
1302 if locked: 1303 self.__lock_depth = self.__lock_depth + 1 1304 gmDispatcher.send(signal='patient_locked') 1305 else: 1306 if self.__lock_depth == 0: 1307 _log.error('lock/unlock imbalance, trying to refcount lock depth below 0') 1308 return 1309 else: 1310 self.__lock_depth = self.__lock_depth - 1 1311 gmDispatcher.send(signal='patient_unlocked')
1312 1313 locked = property(_get_locked, _set_locked) 1314 #--------------------------------------------------------
1315 - def force_unlock(self):
1316 _log.info('forced patient unlock at lock depth [%s]' % self.__lock_depth) 1317 self.__lock_depth = 0 1318 gmDispatcher.send(signal='patient_unlocked')
1319 #-------------------------------------------------------- 1320 # patient change handling 1321 #--------------------------------------------------------
1323 if isinstance(self.patient, gmNull.cNull): 1324 return True 1325 1326 for call_back in self.__pre_selection_callbacks: 1327 try: 1328 successful = call_back() 1329 except: 1330 _log.exception('callback [%s] failed', call_back) 1331 print "*** pre-selection callback failed ***" 1332 print type(call_back) 1333 print call_back 1334 return False 1335 1336 if not successful: 1337 _log.debug('callback [%s] returned False', call_back) 1338 return False 1339 1340 return True
1341 #--------------------------------------------------------
1343 """Sends signal when another patient is about to become active. 1344 1345 This does NOT wait for signal handlers to complete. 1346 """ 1347 kwargs = { 1348 'signal': u'pre_patient_selection', 1349 'sender': id(self.__class__), 1350 'pk_identity': self.patient['pk_identity'] 1351 } 1352 gmDispatcher.send(**kwargs)
1353 #--------------------------------------------------------
1355 """Sends signal when another patient has actually been made active.""" 1356 kwargs = { 1357 'signal': u'post_patient_selection', 1358 'sender': id(self.__class__), 1359 'pk_identity': self.patient['pk_identity'] 1360 } 1361 gmDispatcher.send(**kwargs)
1362 #-------------------------------------------------------- 1363 # __getattr__ handling 1364 #--------------------------------------------------------
1365 - def __getattr__(self, attribute):
1366 if attribute == 'patient': 1367 raise AttributeError 1368 if not isinstance(self.patient, gmNull.cNull): 1369 return getattr(self.patient, attribute)
1370 #-------------------------------------------------------- 1371 # __get/setitem__ handling 1372 #--------------------------------------------------------
1373 - def __getitem__(self, attribute = None):
1374 """Return any attribute if known how to retrieve it by proxy. 1375 """ 1376 return self.patient[attribute]
1377 #--------------------------------------------------------
1378 - def __setitem__(self, attribute, value):
1379 self.patient[attribute] = value
1380 #============================================================ 1381 # match providers 1382 #============================================================
1383 -class cMatchProvider_Provider(gmMatchProvider.cMatchProvider_SQL2):
1384 - def __init__(self):
1385 gmMatchProvider.cMatchProvider_SQL2.__init__( 1386 self, 1387 queries = [ 1388 u"""SELECT 1389 pk_staff AS data, 1390 short_alias || ' (' || coalesce(title, '') || ' ' || firstnames || ' ' || lastnames || ')' AS list_label, 1391 short_alias || ' (' || coalesce(title, '') || ' ' || firstnames || ' ' || lastnames || ')' AS field_label 1392 FROM dem.v_staff 1393 WHERE 1394 is_active AND ( 1395 short_alias %(fragment_condition)s OR 1396 firstnames %(fragment_condition)s OR 1397 lastnames %(fragment_condition)s OR 1398 db_user %(fragment_condition)s 1399 ) 1400 """ 1401 ] 1402 ) 1403 self.setThresholds(1, 2, 3)
1404 #============================================================ 1405 # convenience functions 1406 #============================================================
1407 -def create_name(pk_person, firstnames, lastnames, active=False):
1408 queries = [{ 1409 'cmd': u"select dem.add_name(%s, %s, %s, %s)", 1410 'args': [pk_person, firstnames, lastnames, active] 1411 }] 1412 rows, idx = gmPG2.run_rw_queries(queries=queries, return_data=True) 1413 name = cPersonName(aPK_obj = rows[0][0]) 1414 return name
1415 #============================================================
1416 -def create_identity(gender=None, dob=None, lastnames=None, firstnames=None):
1417 1418 cmd1 = u"""INSERT INTO dem.identity (gender, dob) VALUES (%s, %s)""" 1419 cmd2 = u""" 1420 INSERT INTO dem.names ( 1421 id_identity, lastnames, firstnames 1422 ) VALUES ( 1423 currval('dem.identity_pk_seq'), coalesce(%s, 'xxxDEFAULTxxx'), coalesce(%s, 'xxxDEFAULTxxx') 1424 ) RETURNING id_identity""" 1425 rows, idx = gmPG2.run_rw_queries ( 1426 queries = [ 1427 {'cmd': cmd1, 'args': [gender, dob]}, 1428 {'cmd': cmd2, 'args': [lastnames, firstnames]} 1429 ], 1430 return_data = True 1431 ) 1432 ident = cIdentity(aPK_obj=rows[0][0]) 1433 gmHooks.run_hook_script(hook = u'post_person_creation') 1434 return ident
1435 #============================================================
1436 -def create_dummy_identity():
1437 cmd = u"INSERT INTO dem.identity(gender) VALUES ('xxxDEFAULTxxx') RETURNING pk" 1438 rows, idx = gmPG2.run_rw_queries ( 1439 queries = [{'cmd': cmd}], 1440 return_data = True 1441 ) 1442 return gmDemographicRecord.cIdentity(aPK_obj = rows[0][0])
1443 #============================================================
1444 -def set_active_patient(patient=None, forced_reload=False):
1445 """Set active patient. 1446 1447 If patient is -1 the active patient will be UNset. 1448 """ 1449 if isinstance(patient, cPatient): 1450 pat = patient 1451 elif isinstance(patient, cIdentity): 1452 pat = cPatient(aPK_obj=patient['pk_identity']) 1453 # elif isinstance(patient, cStaff): 1454 # pat = cPatient(aPK_obj=patient['pk_identity']) 1455 elif isinstance(patient, gmCurrentPatient): 1456 pat = patient.patient 1457 elif patient == -1: 1458 pat = patient 1459 else: 1460 raise ValueError('<patient> must be either -1, cPatient, cIdentity or gmCurrentPatient instance, is: %s' % patient) 1461 1462 # attempt to switch 1463 try: 1464 gmCurrentPatient(patient = pat, forced_reload = forced_reload) 1465 except: 1466 _log.exception('error changing active patient to [%s]' % patient) 1467 return False 1468 1469 return True
1470 #============================================================ 1471 # gender related 1472 #------------------------------------------------------------
1473 -def get_gender_list():
1474 """Retrieves the list of known genders from the database.""" 1475 global __gender_idx 1476 global __gender_list 1477 1478 if __gender_list is None: 1479 cmd = u"select tag, l10n_tag, label, l10n_label, sort_weight from dem.v_gender_labels order by sort_weight desc" 1480 __gender_list, __gender_idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True) 1481 1482 return (__gender_list, __gender_idx)
1483 #------------------------------------------------------------ 1484 map_gender2mf = { 1485 'm': u'm', 1486 'f': u'f', 1487 'tf': u'f', 1488 'tm': u'm', 1489 'h': u'mf' 1490 } 1491 #------------------------------------------------------------ 1492 # Maps GNUmed related i18n-aware gender specifiers to a unicode symbol. 1493 map_gender2symbol = { 1494 'm': u'\u2642', 1495 'f': u'\u2640', 1496 'tf': u'\u26A5\u2640', 1497 'tm': u'\u26A5\u2642', 1498 'h': u'\u26A5' 1499 # 'tf': u'\u2642\u2640-\u2640', 1500 # 'tm': u'\u2642\u2640-\u2642', 1501 # 'h': u'\u2642\u2640' 1502 } 1503 #------------------------------------------------------------
1504 -def map_gender2string(gender=None):
1505 """Maps GNUmed related i18n-aware gender specifiers to a human-readable string.""" 1506 1507 global __gender2string_map 1508 1509 if __gender2string_map is None: 1510 genders, idx = get_gender_list() 1511 __gender2string_map = { 1512 'm': _('male'), 1513 'f': _('female'), 1514 'tf': u'', 1515 'tm': u'', 1516 'h': u'' 1517 } 1518 for g in genders: 1519 __gender2string_map[g[idx['l10n_tag']]] = g[idx['l10n_label']] 1520 __gender2string_map[g[idx['tag']]] = g[idx['l10n_label']] 1521 1522 return __gender2string_map[gender]
1523 #------------------------------------------------------------
1524 -def map_gender2salutation(gender=None):
1525 """Maps GNUmed related i18n-aware gender specifiers to a human-readable salutation.""" 1526 1527 global __gender2salutation_map 1528 1529 if __gender2salutation_map is None: 1530 genders, idx = get_gender_list() 1531 __gender2salutation_map = { 1532 'm': _('Mr'), 1533 'f': _('Mrs'), 1534 'tf': u'', 1535 'tm': u'', 1536 'h': u'' 1537 } 1538 for g in genders: 1539 __gender2salutation_map[g[idx['l10n_tag']]] = __gender2salutation_map[g[idx['tag']]] 1540 __gender2salutation_map[g[idx['label']]] = __gender2salutation_map[g[idx['tag']]] 1541 __gender2salutation_map[g[idx['l10n_label']]] = __gender2salutation_map[g[idx['tag']]] 1542 1543 return __gender2salutation_map[gender]
1544 #------------------------------------------------------------
1545 -def map_firstnames2gender(firstnames=None):
1546 """Try getting the gender for the given first name.""" 1547 1548 if firstnames is None: 1549 return None 1550 1551 rows, idx = gmPG2.run_ro_queries(queries = [{ 1552 'cmd': u"select gender from dem.name_gender_map where name ilike %(fn)s limit 1", 1553 'args': {'fn': firstnames} 1554 }]) 1555 1556 if len(rows) == 0: 1557 return None 1558 1559 return rows[0][0]
1560 #============================================================
1561 -def get_persons_from_pks(pks=None):
1562 return [ cIdentity(aPK_obj = pk) for pk in pks ]
1563 #============================================================
1564 -def get_person_from_xdt(filename=None, encoding=None, dob_format=None):
1565 from Gnumed.business import gmXdtObjects 1566 return gmXdtObjects.read_person_from_xdt(filename=filename, encoding=encoding, dob_format=dob_format)
1567 #============================================================
1568 -def get_persons_from_pracsoft_file(filename=None, encoding='ascii'):
1569 from Gnumed.business import gmPracSoftAU 1570 return gmPracSoftAU.read_persons_from_pracsoft_file(filename=filename, encoding=encoding)
1571 #============================================================ 1572 # main/testing 1573 #============================================================ 1574 if __name__ == '__main__': 1575 1576 if len(sys.argv) == 1: 1577 sys.exit() 1578 1579 if sys.argv[1] != 'test': 1580 sys.exit() 1581 1582 import datetime 1583 1584 gmI18N.activate_locale() 1585 gmI18N.install_domain() 1586 gmDateTime.init() 1587 1588 #--------------------------------------------------------
1589 - def test_set_active_pat():
1590 1591 ident = cIdentity(1) 1592 print "setting active patient with", ident 1593 set_active_patient(patient=ident) 1594 1595 patient = cPatient(12) 1596 print "setting active patient with", patient 1597 set_active_patient(patient=patient) 1598 1599 pat = gmCurrentPatient() 1600 print pat['dob'] 1601 #pat['dob'] = 'test' 1602 1603 # staff = cStaff() 1604 # print "setting active patient with", staff 1605 # set_active_patient(patient=staff) 1606 1607 print "setting active patient with -1" 1608 set_active_patient(patient=-1)
1609 #--------------------------------------------------------
1610 - def test_dto_person():
1611 dto = cDTO_person() 1612 dto.firstnames = 'Sepp' 1613 dto.lastnames = 'Herberger' 1614 dto.gender = 'male' 1615 dto.dob = pyDT.datetime.now(tz=gmDateTime.gmCurrentLocalTimezone) 1616 print dto 1617 1618 print dto['firstnames'] 1619 print dto['lastnames'] 1620 print dto['gender'] 1621 print dto['dob'] 1622 1623 for key in dto.keys(): 1624 print key
1625 #--------------------------------------------------------
1626 - def test_identity():
1627 # create patient 1628 print '\n\nCreating identity...' 1629 new_identity = create_identity(gender='m', dob='2005-01-01', lastnames='test lastnames', firstnames='test firstnames') 1630 print 'Identity created: %s' % new_identity 1631 1632 print '\nSetting title and gender...' 1633 new_identity['title'] = 'test title'; 1634 new_identity['gender'] = 'f'; 1635 new_identity.save_payload() 1636 print 'Refetching identity from db: %s' % cIdentity(aPK_obj=new_identity['pk_identity']) 1637 1638 print '\nGetting all names...' 1639 for a_name in new_identity.get_names(): 1640 print a_name 1641 print 'Active name: %s' % (new_identity.get_active_name()) 1642 print 'Setting nickname...' 1643 new_identity.set_nickname(nickname='test nickname') 1644 print 'Refetching all names...' 1645 for a_name in new_identity.get_names(): 1646 print a_name 1647 print 'Active name: %s' % (new_identity.get_active_name()) 1648 1649 print '\nIdentity occupations: %s' % new_identity['occupations'] 1650 print 'Creating identity occupation...' 1651 new_identity.link_occupation('test occupation') 1652 print 'Identity occupations: %s' % new_identity['occupations'] 1653 1654 print '\nIdentity addresses: %s' % new_identity.get_addresses() 1655 print 'Creating identity address...' 1656 # make sure the state exists in the backend 1657 new_identity.link_address ( 1658 number = 'test 1234', 1659 street = 'test street', 1660 postcode = 'test postcode', 1661 urb = 'test urb', 1662 state = 'SN', 1663 country = 'DE' 1664 ) 1665 print 'Identity addresses: %s' % new_identity.get_addresses() 1666 1667 print '\nIdentity communications: %s' % new_identity.get_comm_channels() 1668 print 'Creating identity communication...' 1669 new_identity.link_comm_channel('homephone', '1234566') 1670 print 'Identity communications: %s' % new_identity.get_comm_channels()
1671 #--------------------------------------------------------
1672 - def test_name():
1673 for pk in range(1,16): 1674 name = cPersonName(aPK_obj=pk) 1675 print name.description 1676 print ' ', name
1677 #--------------------------------------------------------
1678 - def test_gender_list():
1679 genders, idx = get_gender_list() 1680 print "\n\nRetrieving gender enum (tag, label, weight):" 1681 for gender in genders: 1682 print "%s, %s, %s" % (gender[idx['tag']], gender[idx['l10n_label']], gender[idx['sort_weight']])
1683 #-------------------------------------------------------- 1684 #test_dto_person() 1685 #test_identity() 1686 #test_set_active_pat() 1687 #test_search_by_dto() 1688 #test_name() 1689 test_gender_list() 1690 1691 #map_gender2salutation('m') 1692 # module functions 1693 1694 #comms = get_comm_list() 1695 #print "\n\nRetrieving communication media enum (id, description): %s" % comms 1696 1697 #============================================================ 1698