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