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