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

Source Code for Module Gnumed.business.gmDemographicRecord

  1  """GNUmed demographics object. 
  2   
  3  This is a patient object intended to let a useful client-side 
  4  API crystallize from actual use in true XP fashion. 
  5   
  6  license: GPL 
  7  """ 
  8  #============================================================ 
  9  __version__ = "$Revision: 1.106 $" 
 10  __author__ = "K.Hilbert <Karsten.Hilbert@gmx.net>, I.Haywood <ihaywood@gnu.org>" 
 11   
 12  # stdlib 
 13  import sys, os.path, time, string, logging 
 14   
 15   
 16  # GNUmed 
 17  if __name__ == '__main__': 
 18          sys.path.insert(0, '../../') 
 19  from Gnumed.pycommon import gmDispatcher, gmBusinessDBObject, gmPG2, gmTools 
 20   
 21   
 22  _log = logging.getLogger('gm.business') 
 23  _log.info(__version__) 
 24   
 25  #============================================================ 
26 -def get_countries():
27 cmd = u""" 28 select 29 _(name) as l10n_country, name, code, deprecated 30 from dem.country 31 order by l10n_country""" 32 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}]) 33 return rows
34 #============================================================
35 -def get_country_for_region(region=None):
36 cmd = u""" 37 SELECT code_country, l10n_country FROM dem.v_state WHERE l10n_state = %(region)s 38 union 39 SELECT code_country, l10n_country FROM dem.v_state WHERE state = %(region)s 40 """ 41 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'region': region}}]) 42 return rows
43 #============================================================
44 -def delete_province(province=None, delete_urbs=False):
45 46 args = {'prov': province} 47 48 queries = [] 49 if delete_urbs: 50 queries.append ({ 51 'cmd': u""" 52 delete from dem.urb du 53 where 54 du.id_state = %(prov)s 55 and 56 not exists (select 1 from dem.street ds where ds.id_urb = du.id)""", 57 'args': args 58 }) 59 60 queries.append ({ 61 'cmd': u""" 62 delete from dem.state ds 63 where 64 ds.id = %(prov)s 65 and 66 not exists (select 1 from dem.urb du where du.id_state = ds.id)""", 67 'args': args 68 }) 69 70 gmPG2.run_rw_queries(queries = queries) 71 72 return True
73 #------------------------------------------------------------
74 -def create_province(name=None, code=None, country=None):
75 76 args = {'code': code, 'country': country, 'name': name} 77 78 cmd = u"""SELECT EXISTS (SELECT 1 FROM dem.state WHERE name = %(name)s)""" 79 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 80 81 if rows[0][0]: 82 return 83 84 cmd = u""" 85 INSERT INTO dem.state ( 86 code, country, name 87 ) VALUES ( 88 %(code)s, %(country)s, %(name)s 89 )""" 90 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
91 #------------------------------------------------------------
92 -def get_provinces():
93 cmd = u""" 94 select 95 l10n_state, l10n_country, state, code_state, code_country, pk_state, country_deprecated 96 from dem.v_state 97 order by l10n_country, l10n_state""" 98 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}]) 99 return rows
100 #============================================================ 101 # address related classes 102 #------------------------------------------------------------
103 -class cAddress(gmBusinessDBObject.cBusinessDBObject):
104 """A class representing an address as an entity in itself. 105 106 We consider addresses to be self-complete "labels" for locations. 107 It does not depend on any people potentially living there. Thus 108 an address can get attached to as many people as we want to 109 signify that that is their place of residence/work/... 110 111 This class acts on the address as an entity. Therefore it can 112 modify the address fields. Think carefully about *modifying* 113 addresses attached to people, though. Most times when you think 114 person.modify_address() what you *really* want is as sequence of 115 person.unlink_address(old) and person.link_address(new). 116 117 Modifying an address may or may not be the proper thing to do as 118 it will transparently modify the address for *all* the people to 119 whom it is attached. In many cases you will want to create a *new* 120 address and link it to a person instead of the old address. 121 """ 122 _cmd_fetch_payload = u"select * from dem.v_address where pk_address=%s" 123 _cmds_store_payload = [ 124 u"""update dem.address set 125 aux_street = %(notes_street)s, 126 subunit = %(subunit)s, 127 addendum = %(notes_subunit)s, 128 lat_lon = %(lat_lon_street)s 129 where id=%(pk_address)s and xmin=%(xmin_address)s""", 130 u"select xmin as xmin_address from dem.address where id=%(pk_address)s" 131 ] 132 _updatable_fields = ['notes_street', 'subunit', 'notes_subunit', 'lat_lon_address']
133 #------------------------------------------------------------
134 -def address_exists(country=None, state=None, urb=None, suburb=None, postcode=None, street=None, number=None, subunit=None, notes_street=None, notes_subunit=None):
135 136 where_parts = [u""" 137 code_country = %(country)s and 138 code_state = %(state)s and 139 urb = %(urb)s and 140 postcode = %(postcode)s and 141 street = %(street)s and 142 number = %(number)s""" 143 ] 144 145 if suburb is None: 146 where_parts.append(u"suburb is %(suburb)s") 147 else: 148 where_parts.append(u"suburb = %(suburb)s") 149 150 if notes_street is None: 151 where_parts.append(u"notes_street is %(notes_street)s") 152 else: 153 where_parts.append(u"notes_street = %(notes_street)s") 154 155 if subunit is None: 156 where_parts.append(u"subunit is %(subunit)s") 157 else: 158 where_parts.append(u"subunit = %(subunit)s") 159 160 if notes_subunit is None: 161 where_parts.append(u"notes_subunit is %(notes_subunit)s") 162 else: 163 where_parts.append(u"notes_subunit = %(notes_subunit)s") 164 165 cmd = u"select pk_address from dem.v_address where %s" % u" and ".join(where_parts) 166 data = { 167 'country': country, 168 'state': state, 169 'urb': urb, 170 'suburb': suburb, 171 'postcode': postcode, 172 'street': street, 173 'notes_street': notes_street, 174 'number': number, 175 'subunit': subunit, 176 'notes_subunit': notes_subunit 177 } 178 179 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': data}]) 180 181 if len(rows) == 0: 182 return None 183 return rows[0][0]
184 #------------------------------------------------------------
185 -def create_address(country=None, state=None, urb=None, suburb=None, postcode=None, street=None, number=None, subunit=None):
186 187 if suburb is not None: 188 suburb = gmTools.none_if(suburb.strip(), u'') 189 190 pk_address = address_exists ( 191 country = country, 192 state = state, 193 urb = urb, 194 suburb = suburb, 195 postcode = postcode, 196 street = street, 197 number = number, 198 subunit = subunit 199 ) 200 if pk_address is not None: 201 return cAddress(aPK_obj=pk_address) 202 203 cmd = u""" 204 select dem.create_address ( 205 %(number)s, 206 %(street)s, 207 %(postcode)s, 208 %(urb)s, 209 %(state)s, 210 %(country)s, 211 %(subunit)s 212 )""" 213 args = { 214 'number': number, 215 'street': street, 216 'postcode': postcode, 217 'urb': urb, 218 'state': state, 219 'country': country, 220 'subunit': subunit 221 } 222 queries = [{'cmd': cmd, 'args': args}] 223 224 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True) 225 adr = cAddress(aPK_obj=rows[0][0]) 226 227 if suburb is not None: 228 queries = [{ 229 # CAVE: suburb will be ignored if there already is one 230 'cmd': u"update dem.street set suburb = %(suburb)s where id=%(pk_street)s and suburb is Null", 231 'args': {'suburb': suburb, 'pk_street': adr['pk_street']} 232 }] 233 rows, idx = gmPG2.run_rw_queries(queries = queries) 234 235 return adr
236 #------------------------------------------------------------
237 -def delete_address(address=None):
238 cmd = u"delete from dem.address where id=%s" 239 rows, idx = gmPG2.run_rw_queries(queries=[{'cmd': cmd, 'args': [address['pk_address']]}]) 240 return True
241 #------------------------------------------------------------
242 -def get_address_types(identity=None):
243 cmd = u'select id as pk, name, _(name) as l10n_name from dem.address_type' 244 rows, idx = gmPG2.run_rw_queries(queries=[{'cmd': cmd}]) 245 return rows
246 #------------------------------------------------------------
247 -def get_addresses(order_by=None):
248 249 if order_by is None: 250 order_by = u'' 251 else: 252 order_by = u'ORDER BY %s' % order_by 253 254 cmd = u"SELECT * FROM dem.v_address %s" % order_by 255 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True) 256 return [ cAddress(row = {'data': r, 'idx': idx, 'pk_field': u'pk_address'}) for r in rows ]
257 258 #===================================================================
259 -class cPatientAddress(gmBusinessDBObject.cBusinessDBObject):
260 261 _cmd_fetch_payload = u"SELECT * FROM dem.v_pat_addresses WHERE pk_address = %s" 262 _cmds_store_payload = [ 263 u"""UPDATE dem.lnk_person_org_address SET 264 id_type = %(pk_address_type)s 265 WHERE 266 id = %(pk_lnk_person_org_address)s 267 AND 268 xmin = %(xmin_lnk_person_org_address)s 269 RETURNING 270 xmin AS xmin_lnk_person_org_address 271 """ 272 # ,u"""select xmin from dem.lnk_person_org_address where id=%(pk_lnk_person_org_address)s""" 273 ] 274 _updatable_fields = ['pk_address_type'] 275 #---------------------------------------------------------------
276 - def get_identities(self, same_lastname=False):
277 pass
278 #=================================================================== 279 # communication channels API 280 #-------------------------------------------------------------------
281 -class cCommChannel(gmBusinessDBObject.cBusinessDBObject):
282 283 _cmd_fetch_payload = u"select * from dem.v_person_comms where pk_lnk_identity2comm = %s" 284 _cmds_store_payload = [ 285 u"""update dem.lnk_identity2comm set 286 fk_address = %(pk_address)s, 287 fk_type = dem.create_comm_type(%(comm_type)s), 288 url = %(url)s, 289 is_confidential = %(is_confidential)s 290 where pk = %(pk_lnk_identity2comm)s and xmin = %(xmin_lnk_identity2comm)s 291 """, 292 u"select xmin as xmin_lnk_identity2comm from dem.lnk_identity2comm where pk = %(pk_lnk_identity2comm)s" 293 ] 294 _updatable_fields = ['pk_address', 'url', 'comm_type', 'is_confidential']
295 #-------------------------------------------------------------------
296 -def create_comm_channel(comm_medium=None, url=None, is_confidential=False, pk_channel_type=None, pk_identity=None):
297 """Create a communications channel for a patient.""" 298 299 if url is None: 300 return None 301 302 # FIXME: create comm type if necessary 303 args = {'pat': pk_identity, 'url': url, 'secret': is_confidential} 304 305 if pk_channel_type is None: 306 args['type'] = comm_medium 307 cmd = u"""insert into dem.lnk_identity2comm ( 308 fk_identity, 309 url, 310 fk_type, 311 is_confidential 312 ) values ( 313 %(pat)s, 314 %(url)s, 315 dem.create_comm_type(%(type)s), 316 %(secret)s 317 )""" 318 else: 319 args['type'] = pk_channel_type 320 cmd = u"""insert into dem.lnk_identity2comm ( 321 fk_identity, 322 url, 323 fk_type, 324 is_confidential 325 ) values ( 326 %(pat)s, 327 %(url)s, 328 %(type)s, 329 %(secret)s 330 )""" 331 332 rows, idx = gmPG2.run_rw_queries ( 333 queries = [ 334 {'cmd': cmd, 'args': args}, 335 {'cmd': u"select * from dem.v_person_comms where pk_lnk_identity2comm = currval(pg_get_serial_sequence('dem.lnk_identity2comm', 'pk'))"} 336 ], 337 return_data = True, 338 get_col_idx = True 339 ) 340 341 return cCommChannel(row = {'pk_field': 'pk_lnk_identity2comm', 'data': rows[0], 'idx': idx})
342 #-------------------------------------------------------------------
343 -def delete_comm_channel(pk=None, pk_patient=None):
344 cmd = u"DELETE FROM dem.lnk_identity2comm WHERE pk = %(pk)s AND fk_identity = %(pat)s" 345 args = {'pk': pk, 'pat': pk_patient} 346 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
347 #------------------------------------------------------------------- 348 __comm_channel_types = None 349
350 -def get_comm_channel_types():
351 global __comm_channel_types 352 if __comm_channel_types is None: 353 cmd = u"select pk, _(description) from dem.enum_comm_types" 354 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}]) 355 __comm_channel_types = rows 356 return __comm_channel_types
357 #------------------------------------------------------------------- 358 359 #===================================================================
360 -class cOrg (gmBusinessDBObject.cBusinessDBObject):
361 """ 362 Organisations 363 364 This is also the common ancestor of cIdentity, self._table is used to 365 hide the difference. 366 The aim is to be able to sanely write code which doesn't care whether 367 its talking to an organisation or an individual""" 368 _table = "org" 369 370 _cmd_fetch_payload = "select *, xmin from dem.org where id=%s" 371 _cmds_lock_rows_for_update = ["select 1 from dem.org where id=%(id)s and xmin=%(xmin)s"] 372 _cmds_store_payload = [ 373 """update dem.org set 374 description=%(description)s, 375 id_category=(select id from dem.org_category where description=%(occupation)s) 376 where id=%(id)s""", 377 "select xmin from dem.org where id=%(id)s" 378 ] 379 _updatable_fields = ["description", "occupation"] 380 _service = 'personalia' 381 #------------------------------------------------------------------
382 - def cleanup (self):
383 pass
384 #------------------------------------------------------------------
385 - def export_demographics (self):
386 if not self.__cache.has_key ('addresses'): 387 self['addresses'] 388 if not self.__cache.has_key ('comms'): 389 self['comms'] 390 return self.__cache
391 #--------------------------------------------------------------------
392 - def get_members (self):
393 """ 394 Returns a list of (address dict, cIdentity) tuples 395 """ 396 cmd = """select 397 vba.id, 398 vba.number, 399 vba.addendum, 400 vba.street, 401 vba.urb, 402 vba.postcode, 403 at.name, 404 lpoa.id_type, 405 vbp.pk_identity, 406 title, 407 firstnames, 408 lastnames, 409 dob, 410 cob, 411 gender, 412 pupic, 413 pk_marital_status, 414 marital_status, 415 karyotype, 416 xmin_identity, 417 preferred 418 from 419 dem.v_basic_address vba, 420 dem.lnk_person_org_address lpoa, 421 dem.address_type at, 422 dem.v_basic_person vbp 423 where 424 lpoa.id_address = vba.id 425 and lpoa.id_type = at.id 426 and lpoa.id_identity = vbp.pk_identity 427 and lpoa.id_org = %%s 428 """ 429 430 rows, idx = gmPG.run_ro_query('personalia', cmd, 1, self.getId ()) 431 if rows is None: 432 return [] 433 elif len(rows) == 0: 434 return [] 435 else: 436 return [({'pk':i[0], 'number':i[1], 'addendum':i[2], 'street':i[3], 'city':i[4], 'postcode':i[5], 'type':i[6], 'id_type':i[7]}, cIdentity (row = {'data':i[8:], 'id':idx[8:], 'pk_field':'id'})) for i in rows]
437 #------------------------------------------------------------
438 - def set_member (self, person, address):
439 """ 440 Binds a person to this organisation at this address. 441 person is a cIdentity object 442 address is a dict of {'number', 'street', 'addendum', 'city', 'postcode', 'type'} 443 type is one of the IDs returned by getAddressTypes 444 """ 445 cmd = "insert into dem.lnk_person_org_address (id_type, id_address, id_org, id_identity) values (%(type)s, dem.create_address (%(number)s, %(addendum)s, %(street)s, %(city)s, %(postcode)s), %(org_id)s, %(pk_identity)s)" 446 address['pk_identity'] = person['pk_identity'] 447 address['org_id'] = self.getId() 448 if not id_addr: 449 return (False, None) 450 return gmPG.run_commit2 ('personalia', [(cmd, [address])])
451 #------------------------------------------------------------ 455 #----------------------------------------------------------------------
456 - def getId (self):
457 """ 458 Hide the difference between org.id and v_basic_person.pk_identity 459 """ 460 return self['id']
461 #==============================================================================
462 -def get_time_tuple (mx):
463 """ 464 wrap mx.DateTime brokenness 465 Returns 9-tuple for use with pyhon time functions 466 """ 467 return [ int(x) for x in str(mx).split(' ')[0].split('-') ] + [0,0,0, 0,0,0]
468 #----------------------------------------------------------------
469 -def getAddressTypes():
470 """Gets a dict matching address types to their ID""" 471 row_list = gmPG.run_ro_query('personalia', "select name, id from dem.address_type") 472 if row_list is None: 473 return {} 474 if len(row_list) == 0: 475 return {} 476 return dict (row_list)
477 #----------------------------------------------------------------
478 -def getMaritalStatusTypes():
479 """Gets a dictionary matching marital status types to their internal ID""" 480 row_list = gmPG.run_ro_query('personalia', "select name, pk from dem.marital_status") 481 if row_list is None: 482 return {} 483 if len(row_list) == 0: 484 return {} 485 return dict(row_list)
486 #------------------------------------------------------------------
487 -def getRelationshipTypes():
488 """Gets a dictionary of relationship types to internal id""" 489 row_list = gmPG.run_ro_query('personalia', "select description, id from dem.relation_types") 490 if row_list is None: 491 return None 492 if len (row_list) == 0: 493 return None 494 return dict(row_list)
495 496 #----------------------------------------------------------------
497 -def getUrb (id_urb):
498 cmd = """ 499 select 500 dem.state.name, 501 dem.urb.postcode 502 from 503 dem.urb, 504 dem.state 505 where 506 dem.urb.id = %s and 507 dem.urb.id_state = dem.state.id""" 508 row_list = gmPG.run_ro_query('personalia', cmd, None, id_urb) 509 if not row_list: 510 return None 511 else: 512 return (row_list[0][0], row_list[0][1])
513
514 -def getStreet (id_street):
515 cmd = """ 516 select 517 dem.state.name, 518 coalesce (dem.street.postcode, dem.urb.postcode), 519 dem.urb.name 520 from 521 dem.urb, 522 dem.state, 523 dem.street 524 where 525 dem.street.id = %s and 526 dem.street.id_urb = dem.urb.id and 527 dem.urb.id_state = dem.state.id 528 """ 529 row_list = gmPG.run_ro_query('personalia', cmd, None, id_street) 530 if not row_list: 531 return None 532 else: 533 return (row_list[0][0], row_list[0][1], row_list[0][2])
534
535 -def getCountry (country_code):
536 row_list = gmPG.run_ro_query('personalia', "select name from dem.country where code = %s", None, country_code) 537 if not row_list: 538 return None 539 else: 540 return row_list[0][0]
541 #-------------------------------------------------------------------------------
542 -def get_town_data (town):
543 row_list = gmPG.run_ro_query ('personalia', """ 544 select 545 dem.urb.postcode, 546 dem.state.code, 547 dem.state.name, 548 dem.country.code, 549 dem.country.name 550 from 551 dem.urb, 552 dem.state, 553 dem.country 554 where 555 dem.urb.name = %s and 556 dem.urb.id_state = dem.state.id and 557 dem.state.country = dem.country.code""", None, town) 558 if not row_list: 559 return (None, None, None, None, None) 560 else: 561 return tuple (row_list[0])
562 #============================================================ 563 # callbacks 564 #------------------------------------------------------------
565 -def _post_patient_selection(**kwargs):
566 print "received post_patient_selection notification" 567 print kwargs['kwds']
568 #============================================================ 569 570 #============================================================ 571 # main 572 #------------------------------------------------------------ 573 if __name__ == "__main__": 574 575 if len(sys.argv) < 2: 576 sys.exit() 577 578 import random 579 #--------------------------------------------------------
580 - def test_address_exists():
581 exists = address_exists ( 582 country ='Germany', 583 state ='Sachsen', 584 urb ='Leipzig', 585 suburb ='Sellerhausen', 586 postcode ='04318', 587 street = u'Cunnersdorfer Strasse', 588 number = '11', 589 notes_subunit = '4.Stock rechts' 590 ) 591 if exists is None: 592 print "address does not exist" 593 else: 594 print "address exists, primary key:", exists
595 #--------------------------------------------------------
596 - def test_create_address():
597 address = create_address ( 598 country ='DE', 599 state ='SN', 600 urb ='Leipzig', 601 suburb ='Sellerhausen', 602 postcode ='04318', 603 street = u'Cunnersdorfer Strasse', 604 number = '11' 605 # ,notes_subunit = '4.Stock rechts' 606 ) 607 print "created existing address" 608 print address 609 610 su = str(random.random()) 611 612 address = create_address ( 613 country ='DE', 614 state = 'SN', 615 urb ='Leipzig', 616 suburb ='Sellerhausen', 617 postcode ='04318', 618 street = u'Cunnersdorfer Strasse', 619 number = '11', 620 # notes_subunit = '4.Stock rechts', 621 subunit = su 622 ) 623 print "created new address with subunit", su 624 print address 625 print "deleted address:", delete_address(address)
626 #--------------------------------------------------------
627 - def test_get_countries():
628 for c in get_countries(): 629 print c
630 #--------------------------------------------------------
631 - def test_get_country_for_region():
632 region = raw_input("Please enter a region: ") 633 print "country for region [%s] is: %s" % (region, get_country_for_region(region = region))
634 #-------------------------------------------------------- 635 if sys.argv[1] != 'test': 636 sys.exit() 637 638 #gmPG2.get_connection() 639 640 #test_address_exists() 641 #test_create_address() 642 #test_get_countries() 643 test_get_country_for_region() 644 645 sys.exit() 646 647 gmDispatcher.connect(_post_patient_selection, 'post_patient_selection') 648 while 1: 649 pID = raw_input('a patient: ') 650 if pID == '': 651 break 652 try: 653 print pID 654 myPatient = gmPerson.cIdentity (aPK_obj = pID) 655 except: 656 _log.exception('Unable to set up patient with ID [%s]' % pID) 657 print "patient", pID, "can not be set up" 658 continue 659 print "ID ", myPatient.ID 660 print "name ", myPatient['description'] 661 print "name ", myPatient['description_gender'] 662 print "title ", myPatient['title'] 663 print "dob ", myPatient['dob'] 664 print "med age ", myPatient['medical_age'] 665 for adr in myPatient.get_addresses(): 666 print "address ", adr 667 print "--------------------------------------" 668 #============================================================ 669