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