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