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