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 from Gnumed.business import gmMedDoc
27
28
29 _log = logging.getLogger('gm.business')
30 _log.info(__version__)
31
32
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
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
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
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
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
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
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
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
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
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
266
267
268
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
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
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
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
372
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
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
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
441 cmd = "delete from dem.lnk_person_org_address where id_org = %s and id_identity = %s"
442 return gmPG.run_commit2 ('personalia', [(cmd, [self.getId(), person.ID])])
443
445 """
446 Hide the difference between org.id and v_basic_person.pk_identity
447 """
448 return self['id']
449
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
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
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
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
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
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
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
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
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
561
563 print "received post_patient_selection notification"
564 print kwargs['kwds']
565
566
567
568
569
570 if __name__ == "__main__":
571
572 if len(sys.argv) < 2:
573 sys.exit()
574
575 import random
576
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
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
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
618 subunit = su
619 )
620 print "created new address with subunit", su
621 print address
622 print "deleted address:", delete_address(address)
623
627
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
636
637
638
639
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
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
1088