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
14 import os
15 import os.path
16 import logging
17
18
19
20 if __name__ == '__main__':
21 sys.path.insert(0, '../../')
22 from Gnumed.pycommon import gmDispatcher
23 from Gnumed.pycommon import gmBusinessDBObject
24 from Gnumed.pycommon import gmPG2
25 from Gnumed.pycommon import gmTools
26
27
28 _log = logging.getLogger('gm.business')
29 _log.info(__version__)
30
31
32
33
34 _SQL_get_tag_image = u"SELECT * FROM ref.v_tag_images_no_data WHERE %s"
35
36 -class cTagImage(gmBusinessDBObject.cBusinessDBObject):
37
38 _cmd_fetch_payload = _SQL_get_tag_image % u"pk_tag_image = %s"
39 _cmds_store_payload = [
40 u"""
41 UPDATE ref.tag_image SET
42 description = gm.nullify_empty_string(%(description)s),
43 filename = gm.nullify_empty_string(%(filename)s)
44 WHERE
45 pk = %(pk_tag_image)s
46 AND
47 xmin = %(xmin_tag_image)s
48 RETURNING
49 pk as pk_tag_image,
50 xmin as xmin_tag_image
51 """
52 ]
53 _updatable_fields = [u'description', u'filename']
54
56
57 if self._payload[self._idx['size']] == 0:
58 return None
59
60 if filename is None:
61 suffix = None
62
63 if self._payload[self._idx['filename']] is not None:
64 name, suffix = os.path.splitext(self._payload[self._idx['filename']])
65 suffix = suffix.strip()
66 if suffix == u'':
67 suffix = None
68
69 filename = gmTools.get_unique_filename (
70 prefix = 'gm-tag_image-',
71 suffix = suffix
72 )
73
74 success = gmPG2.bytea2file (
75 data_query = {
76 'cmd': u'SELECT substring(image from %(start)s for %(size)s) FROM ref.tag_image WHERE pk = %(pk)s',
77 'args': {'pk': self.pk_obj}
78 },
79 filename = filename,
80 chunk_size = aChunkSize,
81 data_size = self._payload[self._idx['size']]
82 )
83
84 if success:
85 return filename
86
87 return None
88
104
106 if order_by is None:
107 order_by = u'true'
108 else:
109 order_by = 'true ORDER BY %s' % order_by
110
111 cmd = _SQL_get_tag_image % order_by
112 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
113 return [ cTagImage(row = {'data': r, 'idx': idx, 'pk_field': 'pk_tag_image'}) for r in rows ]
114
116
117 args = {u'desc': description, u'img': u''}
118 cmd = u"""
119 INSERT INTO ref.tag_image (
120 description,
121 image
122 ) VALUES (
123 %(desc)s,
124 %(img)s::bytea
125 )
126 RETURNING pk
127 """
128 rows, idx = gmPG2.run_rw_queries (
129 link_obj = link_obj,
130 queries = [{'cmd': cmd, 'args': args}],
131 end_tx = True,
132 return_data = True,
133 get_col_idx = False
134 )
135
136 return cTagImage(aPK_obj = rows[0]['pk'])
137
139 args = {'pk': tag_image}
140 cmd = u"""
141 DELETE FROM ref.tag_image
142 WHERE
143 pk = %(pk)s
144 AND
145 NOT EXISTS (
146 SELECT 1
147 FROM dem.identity_tag
148 WHERE fk_tag = %(pk)s
149 LIMIT 1
150 )
151 RETURNING 1
152 """
153 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
154 if len(rows) == 0:
155 return False
156 return True
157
158
159 _SQL_get_identity_tags = u"""SELECT * FROM dem.v_identity_tags WHERE %s"""
160
162
163 _cmd_fetch_payload = _SQL_get_identity_tags % u"pk_identity_tag = %s"
164 _cmds_store_payload = [
165 u"""
166 UPDATE dem.identity_tag SET
167 fk_tag = %(pk_tag_image)s,
168 comment = gm.nullify_empty_string(%(comment)s)
169 WHERE
170 pk = %(pk_identity_tag)s
171 AND
172 xmin = %(xmin_identity_tag)s
173 RETURNING
174 pk as pk_identity_tag,
175 xmin as xmin_identity_tag
176 """
177 ]
178 _updatable_fields = [u'fk_tag', u'comment']
179
181
182 if self._payload[self._idx['image_size']] == 0:
183 return None
184
185 if filename is None:
186 suffix = None
187
188 if self._payload[self._idx['filename']] is not None:
189 name, suffix = os.path.splitext(self._payload[self._idx['filename']])
190 suffix = suffix.strip()
191 if suffix == u'':
192 suffix = None
193
194 filename = gmTools.get_unique_filename (
195 prefix = 'gm-identity_tag-',
196 suffix = suffix
197 )
198
199 exported = gmPG2.bytea2file (
200 data_query = {
201 'cmd': u'SELECT substring(image from %(start)s for %(size)s) FROM ref.tag_image WHERE pk = %(pk)s',
202 'args': {'pk': self._payload[self._idx['pk_tag_image']]}
203 },
204 filename = filename,
205 chunk_size = aChunkSize,
206 data_size = self._payload[self._idx['image_size']]
207 )
208 if exported:
209 return filename
210
211 return None
212
213
215 cmd = u"""
216 select
217 _(name) as l10n_country, name, code, deprecated
218 from dem.country
219 order by l10n_country"""
220 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}])
221 return rows
222
224 cmd = u"""
225 SELECT code_country, l10n_country FROM dem.v_state WHERE l10n_state = %(region)s
226 union
227 SELECT code_country, l10n_country FROM dem.v_state WHERE state = %(region)s
228 """
229 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'region': region}}])
230 return rows
231
233
234 args = {'prov': province}
235
236 queries = []
237 if delete_urbs:
238 queries.append ({
239 'cmd': u"""
240 delete from dem.urb du
241 where
242 du.id_state = %(prov)s
243 and
244 not exists (select 1 from dem.street ds where ds.id_urb = du.id)""",
245 'args': args
246 })
247
248 queries.append ({
249 'cmd': u"""
250 delete from dem.state ds
251 where
252 ds.id = %(prov)s
253 and
254 not exists (select 1 from dem.urb du where du.id_state = ds.id)""",
255 'args': args
256 })
257
258 gmPG2.run_rw_queries(queries = queries)
259
260 return True
261
263
264 args = {'code': code, 'country': country, 'name': name}
265
266 cmd = u"""SELECT EXISTS (SELECT 1 FROM dem.state WHERE name = %(name)s)"""
267 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
268
269 if rows[0][0]:
270 return
271
272 cmd = u"""
273 INSERT INTO dem.state (
274 code, country, name
275 ) VALUES (
276 %(code)s, %(country)s, %(name)s
277 )"""
278 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
279
281 cmd = u"""
282 select
283 l10n_state, l10n_country, state, code_state, code_country, pk_state, country_deprecated
284 from dem.v_state
285 order by l10n_country, l10n_state"""
286 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}])
287 return rows
288
289
290
291 -class cAddress(gmBusinessDBObject.cBusinessDBObject):
292 """A class representing an address as an entity in itself.
293
294 We consider addresses to be self-complete "labels" for locations.
295 It does not depend on any people potentially living there. Thus
296 an address can get attached to as many people as we want to
297 signify that that is their place of residence/work/...
298
299 This class acts on the address as an entity. Therefore it can
300 modify the address fields. Think carefully about *modifying*
301 addresses attached to people, though. Most times when you think
302 person.modify_address() what you *really* want is as sequence of
303 person.unlink_address(old) and person.link_address(new).
304
305 Modifying an address may or may not be the proper thing to do as
306 it will transparently modify the address for *all* the people to
307 whom it is attached. In many cases you will want to create a *new*
308 address and link it to a person instead of the old address.
309 """
310 _cmd_fetch_payload = u"select * from dem.v_address where pk_address=%s"
311 _cmds_store_payload = [
312 u"""UPDATE dem.address SET
313 aux_street = %(notes_street)s,
314 subunit = %(subunit)s,
315 addendum = %(notes_subunit)s,
316 lat_lon = %(lat_lon_street)s
317 WHERE id = %(pk_address)s AND xmin = %(xmin_address)s""",
318 u"select xmin as xmin_address from dem.address where id=%(pk_address)s"
319 ]
320 _updatable_fields = ['notes_street', 'subunit', 'notes_subunit', 'lat_lon_address']
321
322 -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):
323
324 where_parts = [u"""
325 code_country = %(country)s and
326 code_state = %(state)s and
327 urb = %(urb)s and
328 postcode = %(postcode)s and
329 street = %(street)s and
330 number = %(number)s"""
331 ]
332
333 if suburb is None:
334 where_parts.append(u"suburb is %(suburb)s")
335 else:
336 where_parts.append(u"suburb = %(suburb)s")
337
338 if notes_street is None:
339 where_parts.append(u"notes_street is %(notes_street)s")
340 else:
341 where_parts.append(u"notes_street = %(notes_street)s")
342
343 if subunit is None:
344 where_parts.append(u"subunit is %(subunit)s")
345 else:
346 where_parts.append(u"subunit = %(subunit)s")
347
348 if notes_subunit is None:
349 where_parts.append(u"notes_subunit is %(notes_subunit)s")
350 else:
351 where_parts.append(u"notes_subunit = %(notes_subunit)s")
352
353 cmd = u"select pk_address from dem.v_address where %s" % u" and ".join(where_parts)
354 data = {
355 'country': country,
356 'state': state,
357 'urb': urb,
358 'suburb': suburb,
359 'postcode': postcode,
360 'street': street,
361 'notes_street': notes_street,
362 'number': number,
363 'subunit': subunit,
364 'notes_subunit': notes_subunit
365 }
366
367 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': data}])
368
369 if len(rows) == 0:
370 return None
371 return rows[0][0]
372
373 -def create_address(country=None, state=None, urb=None, suburb=None, postcode=None, street=None, number=None, subunit=None):
374
375 if suburb is not None:
376 suburb = gmTools.none_if(suburb.strip(), u'')
377
378 pk_address = address_exists (
379 country = country,
380 state = state,
381 urb = urb,
382 suburb = suburb,
383 postcode = postcode,
384 street = street,
385 number = number,
386 subunit = subunit
387 )
388 if pk_address is not None:
389 return cAddress(aPK_obj=pk_address)
390
391 cmd = u"""
392 select dem.create_address (
393 %(number)s,
394 %(street)s,
395 %(postcode)s,
396 %(urb)s,
397 %(state)s,
398 %(country)s,
399 %(subunit)s
400 )"""
401 args = {
402 'number': number,
403 'street': street,
404 'postcode': postcode,
405 'urb': urb,
406 'state': state,
407 'country': country,
408 'subunit': subunit
409 }
410 queries = [{'cmd': cmd, 'args': args}]
411
412 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True)
413 adr = cAddress(aPK_obj=rows[0][0])
414
415 if suburb is not None:
416 queries = [{
417
418 'cmd': u"update dem.street set suburb = %(suburb)s where id=%(pk_street)s and suburb is Null",
419 'args': {'suburb': suburb, 'pk_street': adr['pk_street']}
420 }]
421 rows, idx = gmPG2.run_rw_queries(queries = queries)
422
423 return adr
424
426 cmd = u"delete from dem.address where id=%s"
427 rows, idx = gmPG2.run_rw_queries(queries=[{'cmd': cmd, 'args': [address['pk_address']]}])
428 return True
429
431 cmd = u'select id as pk, name, _(name) as l10n_name from dem.address_type'
432 rows, idx = gmPG2.run_rw_queries(queries=[{'cmd': cmd}])
433 return rows
434
436
437 if order_by is None:
438 order_by = u''
439 else:
440 order_by = u'ORDER BY %s' % order_by
441
442 cmd = u"SELECT * FROM dem.v_address %s" % order_by
443 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
444 return [ cAddress(row = {'data': r, 'idx': idx, 'pk_field': u'pk_address'}) for r in rows ]
445
446
448
449 _cmd_fetch_payload = u"SELECT * FROM dem.v_pat_addresses WHERE pk_address = %s"
450 _cmds_store_payload = [
451 u"""UPDATE dem.lnk_person_org_address SET
452 id_type = %(pk_address_type)s
453 WHERE
454 id = %(pk_lnk_person_org_address)s
455 AND
456 xmin = %(xmin_lnk_person_org_address)s
457 RETURNING
458 xmin AS xmin_lnk_person_org_address
459 """
460
461 ]
462 _updatable_fields = ['pk_address_type']
463
466
467
468
470
471 _cmd_fetch_payload = u"select * from dem.v_person_comms where pk_lnk_identity2comm = %s"
472 _cmds_store_payload = [
473 u"""update dem.lnk_identity2comm set
474 fk_address = %(pk_address)s,
475 fk_type = dem.create_comm_type(%(comm_type)s),
476 url = %(url)s,
477 is_confidential = %(is_confidential)s
478 where pk = %(pk_lnk_identity2comm)s and xmin = %(xmin_lnk_identity2comm)s
479 """,
480 u"select xmin as xmin_lnk_identity2comm from dem.lnk_identity2comm where pk = %(pk_lnk_identity2comm)s"
481 ]
482 _updatable_fields = ['pk_address', 'url', 'comm_type', 'is_confidential']
483
484 -def create_comm_channel(comm_medium=None, url=None, is_confidential=False, pk_channel_type=None, pk_identity=None):
485 """Create a communications channel for a patient."""
486
487 if url is None:
488 return None
489
490
491 args = {'pat': pk_identity, 'url': url, 'secret': is_confidential}
492
493 if pk_channel_type is None:
494 args['type'] = comm_medium
495 cmd = u"""insert into dem.lnk_identity2comm (
496 fk_identity,
497 url,
498 fk_type,
499 is_confidential
500 ) values (
501 %(pat)s,
502 %(url)s,
503 dem.create_comm_type(%(type)s),
504 %(secret)s
505 )"""
506 else:
507 args['type'] = pk_channel_type
508 cmd = u"""insert into dem.lnk_identity2comm (
509 fk_identity,
510 url,
511 fk_type,
512 is_confidential
513 ) values (
514 %(pat)s,
515 %(url)s,
516 %(type)s,
517 %(secret)s
518 )"""
519
520 rows, idx = gmPG2.run_rw_queries (
521 queries = [
522 {'cmd': cmd, 'args': args},
523 {'cmd': u"select * from dem.v_person_comms where pk_lnk_identity2comm = currval(pg_get_serial_sequence('dem.lnk_identity2comm', 'pk'))"}
524 ],
525 return_data = True,
526 get_col_idx = True
527 )
528
529 return cCommChannel(row = {'pk_field': 'pk_lnk_identity2comm', 'data': rows[0], 'idx': idx})
530
532 cmd = u"DELETE FROM dem.lnk_identity2comm WHERE pk = %(pk)s AND fk_identity = %(pat)s"
533 args = {'pk': pk, 'pat': pk_patient}
534 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
535
536 __comm_channel_types = None
537
545
546
547
548 -class cOrg (gmBusinessDBObject.cBusinessDBObject):
549 """
550 Organisations
551
552 This is also the common ancestor of cIdentity, self._table is used to
553 hide the difference.
554 The aim is to be able to sanely write code which doesn't care whether
555 its talking to an organisation or an individual"""
556 _table = "org"
557
558 _cmd_fetch_payload = "select *, xmin from dem.org where id=%s"
559 _cmds_lock_rows_for_update = ["select 1 from dem.org where id=%(id)s and xmin=%(xmin)s"]
560 _cmds_store_payload = [
561 """update dem.org set
562 description=%(description)s,
563 id_category=(select id from dem.org_category where description=%(occupation)s)
564 where id=%(id)s""",
565 "select xmin from dem.org where id=%(id)s"
566 ]
567 _updatable_fields = ["description", "occupation"]
568 _service = 'personalia'
569
572
574 if not self.__cache.has_key ('addresses'):
575 self['addresses']
576 if not self.__cache.has_key ('comms'):
577 self['comms']
578 return self.__cache
579
581 """
582 Returns a list of (address dict, cIdentity) tuples
583 """
584 cmd = """select
585 vba.id,
586 vba.number,
587 vba.addendum,
588 vba.street,
589 vba.urb,
590 vba.postcode,
591 at.name,
592 lpoa.id_type,
593 vbp.pk_identity,
594 title,
595 firstnames,
596 lastnames,
597 dob,
598 cob,
599 gender,
600 pupic,
601 pk_marital_status,
602 marital_status,
603 karyotype,
604 xmin_identity,
605 preferred
606 from
607 dem.v_basic_address vba,
608 dem.lnk_person_org_address lpoa,
609 dem.address_type at,
610 dem.v_basic_person vbp
611 where
612 lpoa.id_address = vba.id
613 and lpoa.id_type = at.id
614 and lpoa.id_identity = vbp.pk_identity
615 and lpoa.id_org = %%s
616 """
617
618 rows, idx = gmPG.run_ro_query('personalia', cmd, 1, self.getId ())
619 if rows is None:
620 return []
621 elif len(rows) == 0:
622 return []
623 else:
624 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]
625
627 """
628 Binds a person to this organisation at this address.
629 person is a cIdentity object
630 address is a dict of {'number', 'street', 'addendum', 'city', 'postcode', 'type'}
631 type is one of the IDs returned by getAddressTypes
632 """
633 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)"
634 address['pk_identity'] = person['pk_identity']
635 address['org_id'] = self.getId()
636 if not id_addr:
637 return (False, None)
638 return gmPG.run_commit2 ('personalia', [(cmd, [address])])
639
643
645 """
646 Hide the difference between org.id and v_basic_person.pk_identity
647 """
648 return self['id']
649
651 """
652 wrap mx.DateTime brokenness
653 Returns 9-tuple for use with pyhon time functions
654 """
655 return [ int(x) for x in str(mx).split(' ')[0].split('-') ] + [0,0,0, 0,0,0]
656
658 """Gets a dict matching address types to their ID"""
659 row_list = gmPG.run_ro_query('personalia', "select name, id from dem.address_type")
660 if row_list is None:
661 return {}
662 if len(row_list) == 0:
663 return {}
664 return dict (row_list)
665
667 """Gets a dictionary matching marital status types to their internal ID"""
668 row_list = gmPG.run_ro_query('personalia', "select name, pk from dem.marital_status")
669 if row_list is None:
670 return {}
671 if len(row_list) == 0:
672 return {}
673 return dict(row_list)
674
676 """Gets a dictionary of relationship types to internal id"""
677 row_list = gmPG.run_ro_query('personalia', "select description, id from dem.relation_types")
678 if row_list is None:
679 return None
680 if len (row_list) == 0:
681 return None
682 return dict(row_list)
683
684
686 cmd = """
687 select
688 dem.state.name,
689 dem.urb.postcode
690 from
691 dem.urb,
692 dem.state
693 where
694 dem.urb.id = %s and
695 dem.urb.id_state = dem.state.id"""
696 row_list = gmPG.run_ro_query('personalia', cmd, None, id_urb)
697 if not row_list:
698 return None
699 else:
700 return (row_list[0][0], row_list[0][1])
701
703 cmd = """
704 select
705 dem.state.name,
706 coalesce (dem.street.postcode, dem.urb.postcode),
707 dem.urb.name
708 from
709 dem.urb,
710 dem.state,
711 dem.street
712 where
713 dem.street.id = %s and
714 dem.street.id_urb = dem.urb.id and
715 dem.urb.id_state = dem.state.id
716 """
717 row_list = gmPG.run_ro_query('personalia', cmd, None, id_street)
718 if not row_list:
719 return None
720 else:
721 return (row_list[0][0], row_list[0][1], row_list[0][2])
722
724 row_list = gmPG.run_ro_query('personalia', "select name from dem.country where code = %s", None, country_code)
725 if not row_list:
726 return None
727 else:
728 return row_list[0][0]
729
731 row_list = gmPG.run_ro_query ('personalia', """
732 select
733 dem.urb.postcode,
734 dem.state.code,
735 dem.state.name,
736 dem.country.code,
737 dem.country.name
738 from
739 dem.urb,
740 dem.state,
741 dem.country
742 where
743 dem.urb.name = %s and
744 dem.urb.id_state = dem.state.id and
745 dem.state.country = dem.country.code""", None, town)
746 if not row_list:
747 return (None, None, None, None, None)
748 else:
749 return tuple (row_list[0])
750
751
752
754 print "received post_patient_selection notification"
755 print kwargs['kwds']
756
757
758
759
760
761 if __name__ == "__main__":
762
763 if len(sys.argv) < 2:
764 sys.exit()
765
766 if sys.argv[1] != 'test':
767 sys.exit()
768
769 import random
770
772 exists = address_exists (
773 country ='Germany',
774 state ='Sachsen',
775 urb ='Leipzig',
776 suburb ='Sellerhausen',
777 postcode ='04318',
778 street = u'Cunnersdorfer Strasse',
779 number = '11',
780 notes_subunit = '4.Stock rechts'
781 )
782 if exists is None:
783 print "address does not exist"
784 else:
785 print "address exists, primary key:", exists
786
788 address = create_address (
789 country ='DE',
790 state ='SN',
791 urb ='Leipzig',
792 suburb ='Sellerhausen',
793 postcode ='04318',
794 street = u'Cunnersdorfer Strasse',
795 number = '11'
796
797 )
798 print "created existing address"
799 print address
800
801 su = str(random.random())
802
803 address = create_address (
804 country ='DE',
805 state = 'SN',
806 urb ='Leipzig',
807 suburb ='Sellerhausen',
808 postcode ='04318',
809 street = u'Cunnersdorfer Strasse',
810 number = '11',
811
812 subunit = su
813 )
814 print "created new address with subunit", su
815 print address
816 print "deleted address:", delete_address(address)
817
821
823 region = raw_input("Please enter a region: ")
824 print "country for region [%s] is: %s" % (region, get_country_for_region(region = region))
825
827 if delete_tag_image(tag_image = 9999):
828 print "deleted tag 9999"
829 else:
830 print "did not delete tag 9999"
831 if delete_tag_image(tag_image = 1):
832 print "deleted tag 1"
833 else:
834 print "did not delete tag 1"
835
840
841
842
843
844
845
846
847
848
849 test_tag_images()
850
851 sys.exit()
852
853 gmDispatcher.connect(_post_patient_selection, 'post_patient_selection')
854 while 1:
855 pID = raw_input('a patient: ')
856 if pID == '':
857 break
858 try:
859 print pID
860 myPatient = gmPerson.cIdentity (aPK_obj = pID)
861 except:
862 _log.exception('Unable to set up patient with ID [%s]' % pID)
863 print "patient", pID, "can not be set up"
864 continue
865 print "ID ", myPatient.ID
866 print "name ", myPatient['description']
867 print "name ", myPatient['description_gender']
868 print "title ", myPatient['title']
869 print "dob ", myPatient['dob']
870 print "med age ", myPatient['medical_age']
871 for adr in myPatient.get_addresses():
872 print "address ", adr
873 print "--------------------------------------"
874
875