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 import mx.DateTime as mxDT
20
21
22
23 if __name__ == '__main__':
24 sys.path.insert(0, '../../')
25 from Gnumed.pycommon import gmDispatcher, gmBusinessDBObject, gmPG2, gmTools
26
27
28 _log = logging.getLogger('gm.business')
29 _log.info(__version__)
30
31
33 cmd = u"""
34 select
35 _(name) as l10n_country, name, code, deprecated
36 from dem.country
37 order by l10n_country"""
38 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}])
39 return rows
40
42 cmd = u"""
43 SELECT code_country, l10n_country FROM dem.v_state WHERE l10n_state = %(region)s
44 union
45 SELECT code_country, l10n_country FROM dem.v_state WHERE state = %(region)s
46 """
47 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'region': region}}])
48 return rows
49
51
52 args = {'prov': province}
53
54 queries = []
55 if delete_urbs:
56 queries.append ({
57 'cmd': u"""
58 delete from dem.urb du
59 where
60 du.id_state = %(prov)s
61 and
62 not exists (select 1 from dem.street ds where ds.id_urb = du.id)""",
63 'args': args
64 })
65
66 queries.append ({
67 'cmd': u"""
68 delete from dem.state ds
69 where
70 ds.id = %(prov)s
71 and
72 not exists (select 1 from dem.urb du where du.id_state = ds.id)""",
73 'args': args
74 })
75
76 gmPG2.run_rw_queries(queries = queries)
77
78 return True
79
81
82 args = {'code': code, 'country': country, 'name': name}
83
84 cmd = u"""SELECT EXISTS (SELECT 1 FROM dem.state WHERE name = %(name)s)"""
85 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
86
87 if rows[0][0]:
88 return
89
90 cmd = u"""
91 INSERT INTO dem.state (
92 code, country, name
93 ) VALUES (
94 %(code)s, %(country)s, %(name)s
95 )"""
96 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
97
99 cmd = u"""
100 select
101 l10n_state, l10n_country, state, code_state, code_country, pk_state, country_deprecated
102 from dem.v_state
103 order by l10n_country, l10n_state"""
104 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}])
105 return rows
106
107
108
109 -class cAddress(gmBusinessDBObject.cBusinessDBObject):
110 """A class representing an address as an entity in itself.
111
112 We consider addresses to be self-complete "labels" for locations.
113 It does not depend on any people potentially living there. Thus
114 an address can get attached to as many people as we want to
115 signify that that is their place of residence/work/...
116
117 This class acts on the address as an entity. Therefore it can
118 modify the address fields. Think carefully about *modifying*
119 addresses attached to people, though. Most times when you think
120 person.modify_address() what you *really* want is as sequence of
121 person.unlink_address(old) and person.link_address(new).
122
123 Modifying an address may or may not be the proper thing to do as
124 it will transparently modify the address for *all* the people to
125 whom it is attached. In many cases you will want to create a *new*
126 address and link it to a person instead of the old address.
127 """
128 _cmd_fetch_payload = u"select * from dem.v_address where pk_address=%s"
129 _cmds_store_payload = [
130 u"""update dem.address set
131 aux_street = %(notes_street)s,
132 subunit = %(subunit)s,
133 addendum = %(notes_subunit)s,
134 lat_lon = %(lat_lon_street)s
135 where id=%(pk_address)s and xmin=%(xmin_address)s""",
136 u"select xmin as xmin_address from dem.address where id=%(pk_address)s"
137 ]
138 _updatable_fields = ['notes_street', 'subunit', 'notes_subunit', 'lat_lon_address']
139
140 -def address_exists(country=None, state=None, urb=None, suburb=None, postcode=None, street=None, number=None, subunit=None, notes_street=None, notes_subunit=None):
141
142 where_parts = [u"""
143 code_country = %(country)s and
144 code_state = %(state)s and
145 urb = %(urb)s and
146 postcode = %(postcode)s and
147 street = %(street)s and
148 number = %(number)s"""
149 ]
150
151 if suburb is None:
152 where_parts.append(u"suburb is %(suburb)s")
153 else:
154 where_parts.append(u"suburb = %(suburb)s")
155
156 if notes_street is None:
157 where_parts.append(u"notes_street is %(notes_street)s")
158 else:
159 where_parts.append(u"notes_street = %(notes_street)s")
160
161 if subunit is None:
162 where_parts.append(u"subunit is %(subunit)s")
163 else:
164 where_parts.append(u"subunit = %(subunit)s")
165
166 if notes_subunit is None:
167 where_parts.append(u"notes_subunit is %(notes_subunit)s")
168 else:
169 where_parts.append(u"notes_subunit = %(notes_subunit)s")
170
171 cmd = u"select pk_address from dem.v_address where %s" % u" and ".join(where_parts)
172 data = {
173 'country': country,
174 'state': state,
175 'urb': urb,
176 'suburb': suburb,
177 'postcode': postcode,
178 'street': street,
179 'notes_street': notes_street,
180 'number': number,
181 'subunit': subunit,
182 'notes_subunit': notes_subunit
183 }
184
185 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': data}])
186
187 if len(rows) == 0:
188 return None
189 return rows[0][0]
190
191 -def create_address(country=None, state=None, urb=None, suburb=None, postcode=None, street=None, number=None, subunit=None):
192
193 if suburb is not None:
194 suburb = gmTools.none_if(suburb.strip(), u'')
195
196 pk_address = address_exists (
197 country = country,
198 state = state,
199 urb = urb,
200 suburb = suburb,
201 postcode = postcode,
202 street = street,
203 number = number,
204 subunit = subunit
205 )
206 if pk_address is not None:
207 return cAddress(aPK_obj=pk_address)
208
209 cmd = u"""
210 select dem.create_address (
211 %(number)s,
212 %(street)s,
213 %(postcode)s,
214 %(urb)s,
215 %(state)s,
216 %(country)s,
217 %(subunit)s
218 )"""
219 args = {
220 'number': number,
221 'street': street,
222 'postcode': postcode,
223 'urb': urb,
224 'state': state,
225 'country': country,
226 'subunit': subunit
227 }
228 queries = [{'cmd': cmd, 'args': args}]
229
230 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True)
231 adr = cAddress(aPK_obj=rows[0][0])
232
233 if suburb is not None:
234 queries = [{
235
236 'cmd': u"update dem.street set suburb = %(suburb)s where id=%(pk_street)s and suburb is Null",
237 'args': {'suburb': suburb, 'pk_street': adr['pk_street']}
238 }]
239 rows, idx = gmPG2.run_rw_queries(queries = queries)
240
241 return adr
242
244 cmd = u"delete from dem.address where id=%s"
245 rows, idx = gmPG2.run_rw_queries(queries=[{'cmd': cmd, 'args': [address['pk_address']]}])
246 return True
247
249 cmd = u'select id as pk, name, _(name) as l10n_name from dem.address_type'
250 rows, idx = gmPG2.run_rw_queries(queries=[{'cmd': cmd}])
251 return rows
252
254
255 _cmd_fetch_payload = u"select * from dem.v_pat_addresses where pk_address=%s"
256 _cmds_store_payload = [
257 u"""update dem.lnk_person_org_address set id_type=%(pk_address_type)s
258 where id=%(pk_lnk_person_org_address)s and xmin=%(xmin_lnk_person_org_address)s""",
259 u"""select xmin from dem.lnk_person_org_address where id=%(pk_lnk_person_org_address)s"""
260 ]
261 _updatable_fields = ['pk_address_type']
262
265
266
267
269
270 _cmd_fetch_payload = u"select * from dem.v_person_comms where pk_lnk_identity2comm = %s"
271 _cmds_store_payload = [
272 u"""update dem.lnk_identity2comm set
273 fk_address = %(pk_address)s,
274 fk_type = dem.create_comm_type(%(comm_type)s),
275 url = %(url)s,
276 is_confidential = %(is_confidential)s
277 where pk = %(pk_lnk_identity2comm)s and xmin = %(xmin_lnk_identity2comm)s
278 """,
279 u"select xmin as xmin_lnk_identity2comm from dem.lnk_identity2comm where pk = %(pk_lnk_identity2comm)s"
280 ]
281 _updatable_fields = ['pk_address', 'url', 'comm_type', 'is_confidential']
282
283 -def create_comm_channel(comm_medium=None, url=None, is_confidential=False, pk_channel_type=None, pk_identity=None):
284 """Create a communications channel for a patient."""
285
286 if url is None:
287 return None
288
289
290 args = {'pat': pk_identity, 'url': url, 'secret': is_confidential}
291
292 if pk_channel_type is None:
293 args['type'] = comm_medium
294 cmd = u"""insert into dem.lnk_identity2comm (
295 fk_identity,
296 url,
297 fk_type,
298 is_confidential
299 ) values (
300 %(pat)s,
301 %(url)s,
302 dem.create_comm_type(%(type)s),
303 %(secret)s
304 )"""
305 else:
306 args['type'] = pk_channel_type
307 cmd = u"""insert into dem.lnk_identity2comm (
308 fk_identity,
309 url,
310 fk_type,
311 is_confidential
312 ) values (
313 %(pat)s,
314 %(url)s,
315 %(type)s,
316 %(secret)s
317 )"""
318
319 rows, idx = gmPG2.run_rw_queries (
320 queries = [
321 {'cmd': cmd, 'args': args},
322 {'cmd': u"select * from dem.v_person_comms where pk_lnk_identity2comm = currval(pg_get_serial_sequence('dem.lnk_identity2comm', 'pk'))"}
323 ],
324 return_data = True,
325 get_col_idx = True
326 )
327
328 return cCommChannel(row = {'pk_field': 'pk_lnk_identity2comm', 'data': rows[0], 'idx': idx})
329
331 cmd = u"delete from dem.lnk_identity2comm where pk = %(pk)s and fk_identity = %(pat)s"
332 args = {'pk': pk, 'pat': pk_patient}
333 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
334
335 __comm_channel_types = None
336
344
345
346
347 -class cOrg (gmBusinessDBObject.cBusinessDBObject):
348 """
349 Organisations
350
351 This is also the common ancestor of cIdentity, self._table is used to
352 hide the difference.
353 The aim is to be able to sanely write code which doesn't care whether
354 its talking to an organisation or an individual"""
355 _table = "org"
356
357 _cmd_fetch_payload = "select *, xmin from dem.org where id=%s"
358 _cmds_lock_rows_for_update = ["select 1 from dem.org where id=%(id)s and xmin=%(xmin)s"]
359 _cmds_store_payload = [
360 """update dem.org set
361 description=%(description)s,
362 id_category=(select id from dem.org_category where description=%(occupation)s)
363 where id=%(id)s""",
364 "select xmin from dem.org where id=%(id)s"
365 ]
366 _updatable_fields = ["description", "occupation"]
367 _service = 'personalia'
368
371
373 if not self.__cache.has_key ('addresses'):
374 self['addresses']
375 if not self.__cache.has_key ('comms'):
376 self['comms']
377 return self.__cache
378
380 """
381 Returns a list of (address dict, cIdentity) tuples
382 """
383 cmd = """select
384 vba.id,
385 vba.number,
386 vba.addendum,
387 vba.street,
388 vba.urb,
389 vba.postcode,
390 at.name,
391 lpoa.id_type,
392 vbp.pk_identity,
393 title,
394 firstnames,
395 lastnames,
396 dob,
397 cob,
398 gender,
399 pupic,
400 pk_marital_status,
401 marital_status,
402 karyotype,
403 xmin_identity,
404 preferred
405 from
406 dem.v_basic_address vba,
407 dem.lnk_person_org_address lpoa,
408 dem.address_type at,
409 dem.v_basic_person vbp
410 where
411 lpoa.id_address = vba.id
412 and lpoa.id_type = at.id
413 and lpoa.id_identity = vbp.pk_identity
414 and lpoa.id_org = %%s
415 """
416
417 rows, idx = gmPG.run_ro_query('personalia', cmd, 1, self.getId ())
418 if rows is None:
419 return []
420 elif len(rows) == 0:
421 return []
422 else:
423 return [({'pk':i[0], 'number':i[1], 'addendum':i[2], 'street':i[3], 'city':i[4], 'postcode':i[5], 'type':i[6], 'id_type':i[7]}, cIdentity (row = {'data':i[8:], 'id':idx[8:], 'pk_field':'id'})) for i in rows]
424
426 """
427 Binds a person to this organisation at this address.
428 person is a cIdentity object
429 address is a dict of {'number', 'street', 'addendum', 'city', 'postcode', 'type'}
430 type is one of the IDs returned by getAddressTypes
431 """
432 cmd = "insert into dem.lnk_person_org_address (id_type, id_address, id_org, id_identity) values (%(type)s, dem.create_address (%(number)s, %(addendum)s, %(street)s, %(city)s, %(postcode)s), %(org_id)s, %(pk_identity)s)"
433 address['pk_identity'] = person['pk_identity']
434 address['org_id'] = self.getId()
435 if not id_addr:
436 return (False, None)
437 return gmPG.run_commit2 ('personalia', [(cmd, [address])])
438
440 cmd = "delete from dem.lnk_person_org_address where id_org = %s and id_identity = %s"
441 return gmPG.run_commit2 ('personalia', [(cmd, [self.getId(), person.ID])])
442
444 """
445 Hide the difference between org.id and v_basic_person.pk_identity
446 """
447 return self['id']
448
450 """
451 wrap mx.DateTime brokenness
452 Returns 9-tuple for use with pyhon time functions
453 """
454 return [ int(x) for x in str(mx).split(' ')[0].split('-') ] + [0,0,0, 0,0,0]
455
457 """Gets a dict matching address types to their ID"""
458 row_list = gmPG.run_ro_query('personalia', "select name, id from dem.address_type")
459 if row_list is None:
460 return {}
461 if len(row_list) == 0:
462 return {}
463 return dict (row_list)
464
466 """Gets a dictionary matching marital status types to their internal ID"""
467 row_list = gmPG.run_ro_query('personalia', "select name, pk from dem.marital_status")
468 if row_list is None:
469 return {}
470 if len(row_list) == 0:
471 return {}
472 return dict(row_list)
473
474 -def getExtIDTypes (context = 'p'):
475 """Gets dictionary mapping ext ID names to internal code from the backend for the given context
476 """
477
478 rl = gmPG.run_ro_query('personalia', "select name, pk from dem.enum_ext_id_types where context = %s", None, context)
479 if rl is None:
480 return {}
481 return dict (rl)
482
484 """Gets a dictionary of relationship types to internal id"""
485 row_list = gmPG.run_ro_query('personalia', "select description, id from dem.relation_types")
486 if row_list is None:
487 return None
488 if len (row_list) == 0:
489 return None
490 return dict(row_list)
491
492
494 cmd = """
495 select
496 dem.state.name,
497 dem.urb.postcode
498 from
499 dem.urb,
500 dem.state
501 where
502 dem.urb.id = %s and
503 dem.urb.id_state = dem.state.id"""
504 row_list = gmPG.run_ro_query('personalia', cmd, None, id_urb)
505 if not row_list:
506 return None
507 else:
508 return (row_list[0][0], row_list[0][1])
509
511 cmd = """
512 select
513 dem.state.name,
514 coalesce (dem.street.postcode, dem.urb.postcode),
515 dem.urb.name
516 from
517 dem.urb,
518 dem.state,
519 dem.street
520 where
521 dem.street.id = %s and
522 dem.street.id_urb = dem.urb.id and
523 dem.urb.id_state = dem.state.id
524 """
525 row_list = gmPG.run_ro_query('personalia', cmd, None, id_street)
526 if not row_list:
527 return None
528 else:
529 return (row_list[0][0], row_list[0][1], row_list[0][2])
530
532 row_list = gmPG.run_ro_query('personalia', "select name from dem.country where code = %s", None, country_code)
533 if not row_list:
534 return None
535 else:
536 return row_list[0][0]
537
539 row_list = gmPG.run_ro_query ('personalia', """
540 select
541 dem.urb.postcode,
542 dem.state.code,
543 dem.state.name,
544 dem.country.code,
545 dem.country.name
546 from
547 dem.urb,
548 dem.state,
549 dem.country
550 where
551 dem.urb.name = %s and
552 dem.urb.id_state = dem.state.id and
553 dem.state.country = dem.country.code""", None, town)
554 if not row_list:
555 return (None, None, None, None, None)
556 else:
557 return tuple (row_list[0])
558
559
560
562 print "received post_patient_selection notification"
563 print kwargs['kwds']
564
565
566
567
568
569 if __name__ == "__main__":
570
571 if len(sys.argv) < 2:
572 sys.exit()
573
574 import random
575
577 exists = address_exists (
578 country ='Germany',
579 state ='Sachsen',
580 urb ='Leipzig',
581 suburb ='Sellerhausen',
582 postcode ='04318',
583 street = u'Cunnersdorfer Strasse',
584 number = '11',
585 notes_subunit = '4.Stock rechts'
586 )
587 if exists is None:
588 print "address does not exist"
589 else:
590 print "address exists, primary key:", exists
591
593 address = create_address (
594 country ='DE',
595 state ='SN',
596 urb ='Leipzig',
597 suburb ='Sellerhausen',
598 postcode ='04318',
599 street = u'Cunnersdorfer Strasse',
600 number = '11'
601
602 )
603 print "created existing address"
604 print address
605
606 su = str(random.random())
607
608 address = create_address (
609 country ='DE',
610 state = 'SN',
611 urb ='Leipzig',
612 suburb ='Sellerhausen',
613 postcode ='04318',
614 street = u'Cunnersdorfer Strasse',
615 number = '11',
616
617 subunit = su
618 )
619 print "created new address with subunit", su
620 print address
621 print "deleted address:", delete_address(address)
622
626
628 region = raw_input("Please enter a region: ")
629 print "country for region [%s] is: %s" % (region, get_country_for_region(region = region))
630
631 if sys.argv[1] != 'test':
632 sys.exit()
633
634
635
636
637
638
639 test_get_country_for_region()
640
641 sys.exit()
642
643 gmDispatcher.connect(_post_patient_selection, 'post_patient_selection')
644 while 1:
645 pID = raw_input('a patient: ')
646 if pID == '':
647 break
648 try:
649 print pID
650 myPatient = gmPerson.cIdentity (aPK_obj = pID)
651 except:
652 _log.exception('Unable to set up patient with ID [%s]' % pID)
653 print "patient", pID, "can not be set up"
654 continue
655 print "ID ", myPatient.ID
656 print "name ", myPatient['description']
657 print "name ", myPatient['description_gender']
658 print "title ", myPatient['title']
659 print "dob ", myPatient['dob']
660 print "med age ", myPatient['medical_age']
661 for adr in myPatient.get_addresses():
662 print "address ", adr
663 print "--------------------------------------"
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087