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