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 id_type=%(pk_address_type)s 254 where id=%(pk_lnk_person_org_address)s and xmin=%(xmin_lnk_person_org_address)s""", 255 u"""select xmin from dem.lnk_person_org_address where id=%(pk_lnk_person_org_address)s""" 256 ] 257 _updatable_fields = ['pk_address_type'] 258 #---------------------------------------------------------------
259 - def get_identities(self, same_lastname=False):
260 pass
261 #=================================================================== 262 # communication channels API 263 #-------------------------------------------------------------------
264 -class cCommChannel(gmBusinessDBObject.cBusinessDBObject):
265 266 _cmd_fetch_payload = u"select * from dem.v_person_comms where pk_lnk_identity2comm = %s" 267 _cmds_store_payload = [ 268 u"""update dem.lnk_identity2comm set 269 fk_address = %(pk_address)s, 270 fk_type = dem.create_comm_type(%(comm_type)s), 271 url = %(url)s, 272 is_confidential = %(is_confidential)s 273 where pk = %(pk_lnk_identity2comm)s and xmin = %(xmin_lnk_identity2comm)s 274 """, 275 u"select xmin as xmin_lnk_identity2comm from dem.lnk_identity2comm where pk = %(pk_lnk_identity2comm)s" 276 ] 277 _updatable_fields = ['pk_address', 'url', 'comm_type', 'is_confidential']
278 #-------------------------------------------------------------------
279 -def create_comm_channel(comm_medium=None, url=None, is_confidential=False, pk_channel_type=None, pk_identity=None):
280 """Create a communications channel for a patient.""" 281 282 if url is None: 283 return None 284 285 # FIXME: create comm type if necessary 286 args = {'pat': pk_identity, 'url': url, 'secret': is_confidential} 287 288 if pk_channel_type is None: 289 args['type'] = comm_medium 290 cmd = u"""insert into dem.lnk_identity2comm ( 291 fk_identity, 292 url, 293 fk_type, 294 is_confidential 295 ) values ( 296 %(pat)s, 297 %(url)s, 298 dem.create_comm_type(%(type)s), 299 %(secret)s 300 )""" 301 else: 302 args['type'] = pk_channel_type 303 cmd = u"""insert into dem.lnk_identity2comm ( 304 fk_identity, 305 url, 306 fk_type, 307 is_confidential 308 ) values ( 309 %(pat)s, 310 %(url)s, 311 %(type)s, 312 %(secret)s 313 )""" 314 315 rows, idx = gmPG2.run_rw_queries ( 316 queries = [ 317 {'cmd': cmd, 'args': args}, 318 {'cmd': u"select * from dem.v_person_comms where pk_lnk_identity2comm = currval(pg_get_serial_sequence('dem.lnk_identity2comm', 'pk'))"} 319 ], 320 return_data = True, 321 get_col_idx = True 322 ) 323 324 return cCommChannel(row = {'pk_field': 'pk_lnk_identity2comm', 'data': rows[0], 'idx': idx})
325 #-------------------------------------------------------------------
326 -def delete_comm_channel(pk=None, pk_patient=None):
327 cmd = u"delete from dem.lnk_identity2comm where pk = %(pk)s and fk_identity = %(pat)s" 328 args = {'pk': pk, 'pat': pk_patient} 329 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
330 #------------------------------------------------------------------- 331 __comm_channel_types = None 332
333 -def get_comm_channel_types():
334 global __comm_channel_types 335 if __comm_channel_types is None: 336 cmd = u"select pk, _(description) from dem.enum_comm_types" 337 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}]) 338 __comm_channel_types = rows 339 return __comm_channel_types
340 #------------------------------------------------------------------- 341 342 #===================================================================
343 -class cOrg (gmBusinessDBObject.cBusinessDBObject):
344 """ 345 Organisations 346 347 This is also the common ancestor of cIdentity, self._table is used to 348 hide the difference. 349 The aim is to be able to sanely write code which doesn't care whether 350 its talking to an organisation or an individual""" 351 _table = "org" 352 353 _cmd_fetch_payload = "select *, xmin from dem.org where id=%s" 354 _cmds_lock_rows_for_update = ["select 1 from dem.org where id=%(id)s and xmin=%(xmin)s"] 355 _cmds_store_payload = [ 356 """update dem.org set 357 description=%(description)s, 358 id_category=(select id from dem.org_category where description=%(occupation)s) 359 where id=%(id)s""", 360 "select xmin from dem.org where id=%(id)s" 361 ] 362 _updatable_fields = ["description", "occupation"] 363 _service = 'personalia' 364 #------------------------------------------------------------------
365 - def cleanup (self):
366 pass
367 #------------------------------------------------------------------
368 - def export_demographics (self):
369 if not self.__cache.has_key ('addresses'): 370 self['addresses'] 371 if not self.__cache.has_key ('comms'): 372 self['comms'] 373 return self.__cache
374 #--------------------------------------------------------------------
375 - def get_members (self):
376 """ 377 Returns a list of (address dict, cIdentity) tuples 378 """ 379 cmd = """select 380 vba.id, 381 vba.number, 382 vba.addendum, 383 vba.street, 384 vba.urb, 385 vba.postcode, 386 at.name, 387 lpoa.id_type, 388 vbp.pk_identity, 389 title, 390 firstnames, 391 lastnames, 392 dob, 393 cob, 394 gender, 395 pupic, 396 pk_marital_status, 397 marital_status, 398 karyotype, 399 xmin_identity, 400 preferred 401 from 402 dem.v_basic_address vba, 403 dem.lnk_person_org_address lpoa, 404 dem.address_type at, 405 dem.v_basic_person vbp 406 where 407 lpoa.id_address = vba.id 408 and lpoa.id_type = at.id 409 and lpoa.id_identity = vbp.pk_identity 410 and lpoa.id_org = %%s 411 """ 412 413 rows, idx = gmPG.run_ro_query('personalia', cmd, 1, self.getId ()) 414 if rows is None: 415 return [] 416 elif len(rows) == 0: 417 return [] 418 else: 419 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]
420 #------------------------------------------------------------
421 - def set_member (self, person, address):
422 """ 423 Binds a person to this organisation at this address. 424 person is a cIdentity object 425 address is a dict of {'number', 'street', 'addendum', 'city', 'postcode', 'type'} 426 type is one of the IDs returned by getAddressTypes 427 """ 428 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)" 429 address['pk_identity'] = person['pk_identity'] 430 address['org_id'] = self.getId() 431 if not id_addr: 432 return (False, None) 433 return gmPG.run_commit2 ('personalia', [(cmd, [address])])
434 #------------------------------------------------------------ 438 #----------------------------------------------------------------------
439 - def getId (self):
440 """ 441 Hide the difference between org.id and v_basic_person.pk_identity 442 """ 443 return self['id']
444 #==============================================================================
445 -def get_time_tuple (mx):
446 """ 447 wrap mx.DateTime brokenness 448 Returns 9-tuple for use with pyhon time functions 449 """ 450 return [ int(x) for x in str(mx).split(' ')[0].split('-') ] + [0,0,0, 0,0,0]
451 #----------------------------------------------------------------
452 -def getAddressTypes():
453 """Gets a dict matching address types to their ID""" 454 row_list = gmPG.run_ro_query('personalia', "select name, id from dem.address_type") 455 if row_list is None: 456 return {} 457 if len(row_list) == 0: 458 return {} 459 return dict (row_list)
460 #----------------------------------------------------------------
461 -def getMaritalStatusTypes():
462 """Gets a dictionary matching marital status types to their internal ID""" 463 row_list = gmPG.run_ro_query('personalia', "select name, pk from dem.marital_status") 464 if row_list is None: 465 return {} 466 if len(row_list) == 0: 467 return {} 468 return dict(row_list)
469 #------------------------------------------------------------------
470 -def getExtIDTypes (context = 'p'):
471 """Gets dictionary mapping ext ID names to internal code from the backend for the given context 472 """ 473 # FIXME: error handling 474 rl = gmPG.run_ro_query('personalia', "select name, pk from dem.enum_ext_id_types where context = %s", None, context) 475 if rl is None: 476 return {} 477 return dict (rl)
478 #----------------------------------------------------------------
479 -def getRelationshipTypes():
480 """Gets a dictionary of relationship types to internal id""" 481 row_list = gmPG.run_ro_query('personalia', "select description, id from dem.relation_types") 482 if row_list is None: 483 return None 484 if len (row_list) == 0: 485 return None 486 return dict(row_list)
487 488 #----------------------------------------------------------------
489 -def getUrb (id_urb):
490 cmd = """ 491 select 492 dem.state.name, 493 dem.urb.postcode 494 from 495 dem.urb, 496 dem.state 497 where 498 dem.urb.id = %s and 499 dem.urb.id_state = dem.state.id""" 500 row_list = gmPG.run_ro_query('personalia', cmd, None, id_urb) 501 if not row_list: 502 return None 503 else: 504 return (row_list[0][0], row_list[0][1])
505
506 -def getStreet (id_street):
507 cmd = """ 508 select 509 dem.state.name, 510 coalesce (dem.street.postcode, dem.urb.postcode), 511 dem.urb.name 512 from 513 dem.urb, 514 dem.state, 515 dem.street 516 where 517 dem.street.id = %s and 518 dem.street.id_urb = dem.urb.id and 519 dem.urb.id_state = dem.state.id 520 """ 521 row_list = gmPG.run_ro_query('personalia', cmd, None, id_street) 522 if not row_list: 523 return None 524 else: 525 return (row_list[0][0], row_list[0][1], row_list[0][2])
526
527 -def getCountry (country_code):
528 row_list = gmPG.run_ro_query('personalia', "select name from dem.country where code = %s", None, country_code) 529 if not row_list: 530 return None 531 else: 532 return row_list[0][0]
533 #-------------------------------------------------------------------------------
534 -def get_town_data (town):
535 row_list = gmPG.run_ro_query ('personalia', """ 536 select 537 dem.urb.postcode, 538 dem.state.code, 539 dem.state.name, 540 dem.country.code, 541 dem.country.name 542 from 543 dem.urb, 544 dem.state, 545 dem.country 546 where 547 dem.urb.name = %s and 548 dem.urb.id_state = dem.state.id and 549 dem.state.country = dem.country.code""", None, town) 550 if not row_list: 551 return (None, None, None, None, None) 552 else: 553 return tuple (row_list[0])
554 #============================================================ 555 # callbacks 556 #------------------------------------------------------------
557 -def _post_patient_selection(**kwargs):
558 print "received post_patient_selection notification" 559 print kwargs['kwds']
560 #============================================================ 561 562 #============================================================ 563 # main 564 #------------------------------------------------------------ 565 if __name__ == "__main__": 566 567 if len(sys.argv) < 2: 568 sys.exit() 569 570 import random 571 #--------------------------------------------------------
572 - def test_address_exists():
573 exists = address_exists ( 574 country ='Germany', 575 state ='Sachsen', 576 urb ='Leipzig', 577 suburb ='Sellerhausen', 578 postcode ='04318', 579 street = u'Cunnersdorfer Strasse', 580 number = '11', 581 notes_subunit = '4.Stock rechts' 582 ) 583 if exists is None: 584 print "address does not exist" 585 else: 586 print "address exists, primary key:", exists
587 #--------------------------------------------------------
588 - def test_create_address():
589 address = create_address ( 590 country ='DE', 591 state ='SN', 592 urb ='Leipzig', 593 suburb ='Sellerhausen', 594 postcode ='04318', 595 street = u'Cunnersdorfer Strasse', 596 number = '11' 597 # ,notes_subunit = '4.Stock rechts' 598 ) 599 print "created existing address" 600 print address 601 602 su = str(random.random()) 603 604 address = create_address ( 605 country ='DE', 606 state = 'SN', 607 urb ='Leipzig', 608 suburb ='Sellerhausen', 609 postcode ='04318', 610 street = u'Cunnersdorfer Strasse', 611 number = '11', 612 # notes_subunit = '4.Stock rechts', 613 subunit = su 614 ) 615 print "created new address with subunit", su 616 print address 617 print "deleted address:", delete_address(address)
618 #--------------------------------------------------------
619 - def test_get_countries():
620 for c in get_countries(): 621 print c
622 #--------------------------------------------------------
623 - def test_get_country_for_region():
624 region = raw_input("Please enter a region: ") 625 print "country for region [%s] is: %s" % (region, get_country_for_region(region = region))
626 #-------------------------------------------------------- 627 if sys.argv[1] != 'test': 628 sys.exit() 629 630 #gmPG2.get_connection() 631 632 #test_address_exists() 633 #test_create_address() 634 #test_get_countries() 635 test_get_country_for_region() 636 637 sys.exit() 638 639 gmDispatcher.connect(_post_patient_selection, 'post_patient_selection') 640 while 1: 641 pID = raw_input('a patient: ') 642 if pID == '': 643 break 644 try: 645 print pID 646 myPatient = gmPerson.cIdentity (aPK_obj = pID) 647 except: 648 _log.exception('Unable to set up patient with ID [%s]' % pID) 649 print "patient", pID, "can not be set up" 650 continue 651 print "ID ", myPatient.ID 652 print "name ", myPatient['description'] 653 print "name ", myPatient['description_gender'] 654 print "title ", myPatient['title'] 655 print "dob ", myPatient['dob'] 656 print "med age ", myPatient['medical_age'] 657 for adr in myPatient.get_addresses(): 658 print "address ", adr 659 print "--------------------------------------" 660 #============================================================ 661 # $Log: gmDemographicRecord.py,v $ 662 # Revision 1.106 2010-01-31 18:12:53 ncq 663 # - get-country-for-region 664 # 665 # Revision 1.105 2010/01/31 16:51:21 ncq 666 # - fix get-countries() 667 # 668 # Revision 1.104 2010/01/31 16:32:19 ncq 669 # - get-countries() 670 # 671 # Revision 1.103 2009/11/18 16:10:02 ncq 672 # - more provinces handling 673 # 674 # Revision 1.102 2009/11/17 19:40:40 ncq 675 # - delete-province() 676 # 677 # Revision 1.101 2009/09/29 13:13:41 ncq 678 # - get-address-types 679 # 680 # Revision 1.100 2009/04/24 12:04:25 ncq 681 # - if url is None do not add comm channel 682 # 683 # Revision 1.99 2009/04/21 16:53:42 ncq 684 # - cleanup 685 # 686 # Revision 1.98 2008/12/09 23:47:12 ncq 687 # - adjust to description_gender 688 # 689 # Revision 1.97 2008/08/15 15:55:53 ncq 690 # - get_provinces 691 # 692 # Revision 1.96 2008/02/26 16:24:49 ncq 693 # - remove pk_address from create_comm_channel 694 # 695 # Revision 1.95 2008/02/25 17:29:40 ncq 696 # - cleanup 697 # 698 # Revision 1.94 2008/01/07 19:32:15 ncq 699 # - cleanup, rearrange 700 # - create comm channel API 701 # 702 # Revision 1.93 2007/12/03 20:41:07 ncq 703 # - get_comm_channel_types() 704 # 705 # Revision 1.92 2007/12/02 20:56:37 ncq 706 # - adjust to table changes 707 # 708 # Revision 1.91 2007/11/17 16:10:53 ncq 709 # - improve create_address() 710 # - cleanup and fixes 711 # 712 # Revision 1.90 2007/11/12 22:52:01 ncq 713 # - create_address() now doesn't care about non-changing fields 714 # 715 # Revision 1.89 2007/11/07 22:59:31 ncq 716 # - don't allow editing number on address 717 # 718 # Revision 1.88 2007/03/23 15:01:36 ncq 719 # - no more person['id'] 720 # 721 # Revision 1.87 2007/02/22 16:26:54 ncq 722 # - fix create_address() 723 # 724 # Revision 1.86 2006/11/26 15:44:03 ncq 725 # - do not use es-zet in test suite 726 # 727 # Revision 1.85 2006/11/19 10:58:52 ncq 728 # - fix imports 729 # - add cAddress 730 # - add cPatientAddress 731 # - remove dead match provider code 732 # - add address_exists(), create_address(), delete_address() 733 # - improve test suite 734 # 735 # Revision 1.84 2006/10/25 07:17:40 ncq 736 # - no more gmPG 737 # - no more cClinItem 738 # 739 # Revision 1.83 2006/10/24 13:15:48 ncq 740 # - comment out/remove a bunch of deprecated/unused match providers 741 # 742 # Revision 1.82 2006/07/19 20:25:00 ncq 743 # - gmPyCompat.py is history 744 # 745 # Revision 1.81 2006/06/14 10:22:46 ncq 746 # - create_* stored procs are in schema dem.* now 747 # 748 # Revision 1.80 2006/05/15 13:24:13 ncq 749 # - signal "activating_patient" -> "pre_patient_selection" 750 # - signal "patient_selected" -> "post_patient_selection" 751 # 752 # Revision 1.79 2006/01/07 13:13:46 ncq 753 # - more schema qualifications 754 # 755 # Revision 1.78 2006/01/07 11:23:24 ncq 756 # - must use """ for multi-line string 757 # 758 # Revision 1.77 2006/01/06 10:15:37 ncq 759 # - lots of small fixes adjusting to "dem" schema 760 # 761 # Revision 1.76 2005/10/09 08:11:48 ihaywood 762 # introducing get_town_data (), a convience method to get info that can be inferred from a town's name (in AU) 763 # 764 # Revision 1.75 2005/10/09 02:19:40 ihaywood 765 # the address widget now has the appropriate widget order and behaviour for australia 766 # when os.environ["LANG"] == 'en_AU' (is their a more graceful way of doing this?) 767 # 768 # Remember our postcodes work very differently. 769 # 770 # Revision 1.74 2005/06/07 10:15:47 ncq 771 # - setContext -> set_context 772 # 773 # Revision 1.73 2005/04/25 08:26:48 ncq 774 # - cleanup 775 # 776 # Revision 1.72 2005/04/14 19:14:51 cfmoro 777 # Gender dict was replaced by get_genders method 778 # 779 # Revision 1.71 2005/04/14 18:58:14 cfmoro 780 # Added create occupation method and minor gender map clean up, to replace later by get_gender_list 781 # 782 # Revision 1.70 2005/04/14 08:49:29 ncq 783 # - move cIdentity and dob2medical_age() to gmPerson.py 784 # 785 # Revision 1.69 2005/03/30 21:04:01 cfmoro 786 # id -> pk_identity 787 # 788 # Revision 1.68 2005/03/29 18:55:39 cfmoro 789 # Var name fix 790 # 791 # Revision 1.67 2005/03/20 16:47:26 ncq 792 # - cleanup 793 # 794 # Revision 1.66 2005/03/08 16:41:37 ncq 795 # - properly handle title 796 # 797 # Revision 1.65 2005/03/06 08:17:02 ihaywood 798 # forms: back to the old way, with support for LaTeX tables 799 # 800 # business objects now support generic linked tables, demographics 801 # uses them to the same functionality as before (loading, no saving) 802 # They may have no use outside of demographics, but saves much code already. 803 # 804 # Revision 1.64 2005/02/20 21:00:20 ihaywood 805 # getId () is back 806 # 807 # Revision 1.63 2005/02/20 09:46:08 ihaywood 808 # demographics module with load a patient with no exceptions 809 # 810 # Revision 1.61 2005/02/19 15:04:55 sjtan 811 # 812 # identity.id is now identity.pk_identity, so adapt 813 # 814 # Revision 1.60 2005/02/18 11:16:41 ihaywood 815 # new demographics UI code won't crash the whole client now ;-) 816 # still needs much work 817 # RichardSpace working 818 # 819 # Revision 1.59 2005/02/13 15:46:46 ncq 820 # - trying to keep up to date with schema changes but may conflict with Ian 821 # 822 # Revision 1.58 2005/02/12 14:00:21 ncq 823 # - identity.id -> pk 824 # - v_basic_person.i_id -> i_pk 825 # - likely missed some places here, though 826 # 827 # Revision 1.57 2005/02/03 20:17:18 ncq 828 # - get_demographic_record() -> get_identity() 829 # 830 # Revision 1.56 2005/02/01 10:16:07 ihaywood 831 # refactoring of gmDemographicRecord and follow-on changes as discussed. 832 # 833 # gmTopPanel moves to gmHorstSpace 834 # gmRichardSpace added -- example code at present, haven't even run it myself 835 # (waiting on some icon .pngs from Richard) 836 # 837 # Revision 1.55 2005/01/31 10:37:26 ncq 838 # - gmPatient.py -> gmPerson.py 839 # 840 # Revision 1.54 2004/08/18 09:05:07 ncq 841 # - just some cleanup, double-check _ is defined for epydoc 842 # 843 # Revision 1.53 2004/07/26 14:34:49 sjtan 844 # 845 # numbering correction from labels in gmDemograpics. 846 # 847 # Revision 1.52 2004/06/25 12:37:19 ncq 848 # - eventually fix the import gmI18N issue 849 # 850 # Revision 1.51 2004/06/21 16:02:08 ncq 851 # - cleanup, trying to make epydoc fix do the right thing 852 # 853 # Revision 1.50 2004/06/21 14:48:25 sjtan 854 # 855 # restored some methods that gmContacts depends on, after they were booted 856 # out from gmDemographicRecord with no home to go , works again ; 857 # removed cCatFinder('occupation') instantiating in main module scope 858 # which was a source of complaint , as it still will lazy load anyway. 859 # 860 # Revision 1.49 2004/06/20 15:38:00 ncq 861 # - remove import gettext/_ = gettext.gettext 862 # - import gmI18N handles that if __main__ 863 # - else the importer of gmDemographicRecord has 864 # to handle setting _ 865 # - this is the Right Way as per the docs ! 866 # 867 # Revision 1.48 2004/06/20 06:49:21 ihaywood 868 # changes required due to Epydoc's OCD 869 # 870 # Revision 1.47 2004/06/17 11:36:12 ihaywood 871 # Changes to the forms layer. 872 # Now forms can have arbitrary Python expressions embedded in @..@ markup. 873 # A proper forms HOWTO will appear in the wiki soon 874 # 875 # Revision 1.46 2004/05/30 03:50:41 sjtan 876 # 877 # gmContacts can create/update org, one level of sub-org, org persons, sub-org persons. 878 # pre-alpha or alpha ? Needs cache tune-up . 879 # 880 # Revision 1.45 2004/05/29 12:03:47 sjtan 881 # 882 # OrgCategoryMP for gmContact's category field 883 # 884 # Revision 1.44 2004/05/28 15:05:10 sjtan 885 # 886 # utility functions only called with exactly 2 args in order to fulfill function intent, but do some checking for invalid args. 887 # 888 # Revision 1.43 2004/05/26 12:58:14 ncq 889 # - cleanup, error handling 890 # 891 # Revision 1.42 2004/05/25 16:18:13 sjtan 892 # 893 # move methods for postcode -> urb interaction to gmDemographics so gmContacts can use it. 894 # 895 # Revision 1.41 2004/05/25 16:00:34 sjtan 896 # 897 # move common urb/postcode collaboration to business class. 898 # 899 # Revision 1.40 2004/05/19 11:16:08 sjtan 900 # 901 # allow selecting the postcode for restricting the urb's picklist, and resetting 902 # the postcode for unrestricting the urb picklist. 903 # 904 # Revision 1.39 2004/04/15 09:46:56 ncq 905 # - cleanup, get_lab_data -> get_lab_results 906 # 907 # Revision 1.38 2004/04/11 10:15:56 ncq 908 # - load title in get_names() and use it superceding getFullName 909 # 910 # Revision 1.37 2004/04/10 01:48:31 ihaywood 911 # can generate referral letters, output to xdvi at present 912 # 913 # Revision 1.36 2004/04/07 18:43:47 ncq 914 # - more gender mappings 915 # - *comm_channel -> comm_chan 916 # 917 # Revision 1.35 2004/03/27 04:37:01 ihaywood 918 # lnk_person2address now lnk_person_org_address 919 # sundry bugfixes 920 # 921 # Revision 1.34 2004/03/25 11:01:45 ncq 922 # - getActiveName -> get_names(all=false) 923 # - export_demographics() 924 # 925 # Revision 1.33 2004/03/20 19:43:16 ncq 926 # - do import gmI18N, we need it 927 # - gm2long_gender_map -> map_gender_gm2long 928 # - gmDemo* -> cDemo* 929 # 930 # Revision 1.32 2004/03/20 17:53:15 ncq 931 # - cleanup 932 # 933 # Revision 1.31 2004/03/15 15:35:45 ncq 934 # - optimize getCommChannel() a bit 935 # 936 # Revision 1.30 2004/03/10 12:56:01 ihaywood 937 # fixed sudden loss of main.shadow 938 # more work on referrals, 939 # 940 # Revision 1.29 2004/03/10 00:09:51 ncq 941 # - cleanup imports 942 # 943 # Revision 1.28 2004/03/09 07:34:51 ihaywood 944 # reactivating plugins 945 # 946 # Revision 1.27 2004/03/04 10:41:21 ncq 947 # - comments, cleanup, adapt to minor schema changes 948 # 949 # Revision 1.26 2004/03/03 23:53:22 ihaywood 950 # GUI now supports external IDs, 951 # Demographics GUI now ALPHA (feature-complete w.r.t. version 1.0) 952 # but happy to consider cosmetic changes 953 # 954 # Revision 1.25 2004/03/03 05:24:01 ihaywood 955 # patient photograph support 956 # 957 # Revision 1.24 2004/03/02 10:21:09 ihaywood 958 # gmDemographics now supports comm channels, occupation, 959 # country of birth and martial status 960 # 961 # Revision 1.23 2004/02/26 17:19:59 ncq 962 # - setCommChannel: arg channel -> channel_type 963 # - setCommChannel: we need to read currval() within 964 # the same transaction as the insert to get consistent 965 # results 966 # 967 # Revision 1.22 2004/02/26 02:12:00 ihaywood 968 # comm channel methods 969 # 970 # Revision 1.21 2004/02/25 09:46:20 ncq 971 # - import from pycommon now, not python-common 972 # 973 # Revision 1.20 2004/02/18 15:26:39 ncq 974 # - fix dob2medical_age() 975 # 976 # Revision 1.19 2004/02/18 06:36:04 ihaywood 977 # bugfixes 978 # 979 # Revision 1.18 2004/02/17 10:30:14 ncq 980 # - enhance GetAddresses() to return all types if addr_type is None 981 # 982 # Revision 1.17 2004/02/17 04:04:34 ihaywood 983 # fixed patient creation refeential integrity error 984 # 985 # Revision 1.16 2004/02/14 00:37:10 ihaywood 986 # Bugfixes 987 # - weeks = days / 7 988 # - create_new_patient to maintain xlnk_identity in historica 989 # 990 # Revision 1.15 2003/12/01 01:01:41 ncq 991 # - get_medical_age -> dob2medical_age 992 # 993 # Revision 1.14 2003/11/30 01:06:21 ncq 994 # - add/remove_external_id() 995 # 996 # Revision 1.13 2003/11/23 23:32:01 ncq 997 # - some cleanup 998 # - setTitle now works on identity instead of names 999 # 1000 # Revision 1.12 2003/11/23 14:02:40 sjtan 1001 # 1002 # by setting active=false first, then updating values, side effects from triggers can be avoided; see also 1003 # F_active_name trigger function in server sql script,which fires only if new.active = true . 1004 # 1005 # Revision 1.11 2003/11/22 14:44:17 ncq 1006 # - setTitle now only operates on active names and also doesn't set the name 1007 # to active which would trigger the trigger 1008 # 1009 # Revision 1.10 2003/11/22 14:40:59 ncq 1010 # - setActiveName -> addName 1011 # 1012 # Revision 1.9 2003/11/22 12:53:48 sjtan 1013 # 1014 # modified the setActiveName and setTitle so it works as intended in the face of insurmountable triggers ;) 1015 # 1016 # Revision 1.8 2003/11/20 07:45:45 ncq 1017 # - update names/identity, not v_basic_person in setTitle et al 1018 # 1019 # Revision 1.7 2003/11/20 02:10:50 sjtan 1020 # 1021 # remove 'self' parameter from functions moved into global module namespace. 1022 # 1023 # Revision 1.6 2003/11/20 01:52:03 ncq 1024 # - actually, make that **?** for good measure 1025 # 1026 # Revision 1.5 2003/11/20 01:50:52 ncq 1027 # - return '?' for missing names in getActiveName() 1028 # 1029 # Revision 1.4 2003/11/18 16:44:24 ncq 1030 # - getAddress -> getAddresses 1031 # - can't verify mxDateTime bug with missing time tuple 1032 # - remove getBirthYear, do getDOB() -> mxDateTime -> format 1033 # - get_relative_list -> get_relatives 1034 # - create_dummy_relative -> link_new_relative 1035 # - cleanup 1036 # 1037 # Revision 1.3 2003/11/17 10:56:34 sjtan 1038 # 1039 # synced and commiting. 1040 # 1041 # Revision 1.2 2003/11/04 10:35:22 ihaywood 1042 # match providers in gmDemographicRecord 1043 # 1044 # Revision 1.1 2003/11/03 23:59:55 ncq 1045 # - renamed to avoid namespace pollution with plugin widget 1046 # 1047 # Revision 1.6 2003/10/31 23:21:20 ncq 1048 # - remove old history 1049 # 1050 # Revision 1.5 2003/10/27 15:52:41 ncq 1051 # - if aFormat is None in getDOB() return datetime object, else return formatted string 1052 # 1053 # Revision 1.4 2003/10/26 17:35:04 ncq 1054 # - conceptual cleanup 1055 # - IMHO, patient searching and database stub creation is OUTSIDE 1056 # THE SCOPE OF gmPerson and gmDemographicRecord 1057 # 1058 # Revision 1.3 2003/10/26 16:35:03 ncq 1059 # - clean up as discussed with Ian, merge conflict resolution 1060 # 1061 # Revision 1.2 2003/10/26 11:27:10 ihaywood 1062 # gmPatient is now the "patient stub", all demographics stuff in gmDemographics. 1063 # 1064 # Ergregious breakages are fixed, but needs more work 1065 # 1066 # Revision 1.1 2003/10/25 08:48:06 ihaywood 1067 # Split from gmTmpPatient 1068 # 1069 1070 1071 1072 # old gmTmpPatient log 1073 # Revision 1.41 2003/10/19 10:42:57 ihaywood 1074 # extra functions 1075 # 1076 # Revision 1.40 2003/09/24 08:45:40 ihaywood 1077 # NewAddress now functional 1078 # 1079 # Revision 1.39 2003/09/23 19:38:03 ncq 1080 # - cleanup 1081 # - moved GetAddressesType out of patient class - it's a generic function 1082 # 1083