PyXR

c:\projects\bitpim\src \ native \ egroupware \ egroupware.py



0001 ### BITPIM
0002 ###
0003 ### Copyright (C) 2004 Roger Binns <rogerb@rogerbinns.com>
0004 ###
0005 ### This program is free software; you can redistribute it and/or modify
0006 ### it under the terms of the BitPim license as detailed in the LICENSE file.
0007 ###
0008 ### $Id: egroupware.py 1770 2004-11-22 06:53:49Z rogerb $
0009 
0010 """Be at one with eGroupware
0011 
0012 We talk to eGroupware using its xmlrpc interface.  Unfortunately the interface
0013 has several quality issues, so we try to work around them in this code.
0014 """
0015 
0016 import xmlrpclib
0017 import urlparse
0018 import time
0019 import datetime
0020 
0021 def getsession(url, user, password, domain="default"):
0022 
0023     # fixup whatever the user max have given us
0024     scheme, location, path, query, fragment = urlparse.urlsplit(url)
0025 
0026     if scheme is None and location is None and query is None:
0027         url="http://"+url
0028     
0029     if url[-1]!="/": url+="/"
0030     url+="xmlrpc.php"
0031 
0032     sp=xmlrpclib.ServerProxy(url)
0033 
0034     res=sp.system.login({"username": user, "password": password, "domain": domain})
0035 
0036     if "sessionid" not in res or "kp3" not in res:
0037         raise Exception("Invalid username or password")
0038 
0039     scheme, location, path, query, fragment = urlparse.urlsplit(url)
0040 
0041     if location.find("@")>=0:
0042         location=location[location.find("@")+1:]
0043 
0044     newurl=urlparse.urlunsplit( (scheme, "%s:%s@%s" % (res["sessionid"], res["kp3"], location), path, query, fragment) )
0045     return Session(xmlrpclib.ServerProxy(newurl), res)
0046 
0047 class Session:
0048 
0049     def __init__(self, sp, ifo):
0050         self.sp=sp
0051         self.__ifo=ifo
0052 
0053     def __del__(self):
0054         self.sp.system.logout(self.__ifo)
0055         self.sp=None
0056         self.__ifo=None
0057 
0058     def getyearcalendar(self, year):
0059         return getcalendar((year,), (year,))
0060 
0061     def getcalendar(self, start=(), end=()):
0062         if len(start)!=6 or len(end)!=6:
0063             t=time.localtime()
0064             startdefs=(t[0], 1, 1, 0,0,0)
0065             enddefs=(t[0],12,31,23,59,60)
0066             start=start+startdefs[len(start):]
0067             end=end+enddefs[len(end):]
0068         start="%04d-%02d-%02dT%02d:%02d:%02d" % start
0069         end="%04d-%02d-%02dT%02d:%02d:%02d" % end
0070         for item in self.sp.calendar.bocalendar.search({"start": start, "end": end}):
0071             for k in item.keys():
0072                 if isinstance(item[k], xmlrpclib.DateTime):
0073                     v=str(item[k])
0074                     v=[int(x) for x in v[0:4], v[5:7], v[8:10], v[11:13], v[14:16], v[17:19]]
0075                     if v==[0,0,0,0,0,0]:
0076                         del item[k]
0077                     else:
0078                         item[k]=datetime.datetime(*v)
0079             yield item
0080 
0081     def doescontactexist(self, id):
0082         try:
0083             return self.sp.addressbook.boaddressbook.read({'id': id})
0084         except xmlrpclib.Fault, f:
0085             # in theory only fault 10 - Entry does not (longer) exist!
0086             # should be looked for.  Unfortunately egroupware has a
0087             # bug and returns fault 9 - Access denied if the id
0088             # doesn't exist.  So we consider any failure to mean
0089             # that the id doesn't exist.  Reported as SF bug #1057984
0090             print "eg contact doesn't exist, fault", f
0091             return False
0092 
0093     def getcontacts(self):
0094         "returns all contacts"
0095         # internally we read them a group at a time
0096         offset=0 
0097         limit=5
0098 
0099         # NB an offset of zero causes egroupware to return ALL contacts ignoring limit!
0100         # This has been filed as bug 1040738 at SourceForge against eGroupware.  It
0101         # won't hurt unless you have huge number of contacts as egroupware will try
0102         # to return all of them at once.  Alternatively make the simple fix as in
0103         # the bug report
0104 
0105         while True:
0106             contacts=self.sp.addressbook.boaddressbook.search({'start': offset, 'limit': limit})
0107             if len(contacts)==0:
0108                 raise StopIteration()
0109             for i in contacts:
0110                 yield i
0111             if len(contacts)<limit:
0112                 raise StopIteration()
0113             offset+=len(contacts)
0114 
0115     def writecontact(self, contact):
0116         "Returns the id of the contact"
0117         # egroupware returns True if the contact was written successfully using the
0118         # existing id, otherwise it returns the new id
0119         res=self.sp.addressbook.boaddressbook.write(contact)
0120         if res is True:
0121             return contact['id']
0122         return res
0123 
0124     def getcontactspbformat(self):
0125         "returns contacts in a format suitable for the BitPim phonebook importer"
0126         # eGroupware gives a nice shine in the UI, but the underlying data is
0127         # somewhat messy
0128         for c in self.getcontacts():
0129             res={}
0130             # egroupware format is very similar to vCard
0131             res['Name']=c['fn']
0132             res['First Name']=c['n_given']
0133             res['Middle Name']=c['n_middle']
0134             res['Last Name']=c['n_family']
0135             res['UniqueSerial-id']=c['id']
0136             res['UniqueSerial-sourcetype']='egroupware'
0137             # addresses
0138             for t,prefix in ("business", "adr_one"), ("home", "adr_two"):
0139                 a={}
0140                 # egroupware has street 2 and 3 in the ui, but doesn't expose them
0141                 # via xmlrpc - reported as SF bug # 1043862
0142                 for p2,k in ("_street", "street"), ("_locality", "city"), ("_region", "state"), \
0143                         ("_postalcode", "postalcode"), ("_countryname", "country"):
0144                     if len(c.get(prefix+p2,"")): a[k]=c[prefix+p2]
0145                 if t=="business" and len(c.get("org_name", "")): a['company']=c["org_name"]
0146                 if len(a):
0147                     a['type']=t
0148                     aa="Address"
0149                     if aa in res:
0150                         aa+="2"
0151                         assert aa not in res
0152                     res[aa]=a
0153             # categories
0154             cats=[]
0155             ccats=c.get("cat_id", "")
0156             if len(ccats): # could be empty string or a dict
0157                 for cat in ccats:
0158                     cats.append(ccats[cat])
0159                 if len(cats):
0160                     res["Categories"]=cats
0161             # email - we ignore the silly egroupware email type field
0162             suf=""
0163             if len(c.get("email","")):
0164                 res["Email Address"]={'email': c['email'], 'type': 'business'}
0165                 suf="2"
0166             if len(c.get("email_home", "")):
0167                 res["Email Address"+suf]={'email': c['email_home'], 'type': 'home'}
0168             # phone numbers
0169             res["Home Phone"]=c['tel_home']
0170             res["Mobile Phone"]=c['tel_cell'] # nb: in eGroupware this is business cell
0171             res["Business Fax"]=c['tel_fax']
0172             res["Pager"]=c['tel_pager'] # nb: in eGroupware this is business pager
0173             res["Business Phone"]=c['tel_work']
0174             # various other fields
0175             res['Notes']=c['note']
0176             res['Business Web Page']=c['url']
0177 
0178             # filter out empty fields
0179             res=dict([(k,v) for k,v in res.items() if len(v)])
0180             yield res
0181 
0182     def getcategories(self):
0183         "Get the list of categories"
0184         # egroupware returns a dict with each key being an asciized integer
0185         # id.  The same field is present in the dict value, so we just return
0186         # the values
0187         return [v for k,v in self.sp.addressbook.boaddressbook.categories(True).items()]
0188     
0189             
0190 if __name__=='__main__':
0191     import sys
0192     import common
0193     s=getsession(*sys.argv[1:])
0194     for v in s.getcategories():
0195         print common.prettyprintdict(v)
0196     #for n,i in enumerate(s.getcontacts()):
0197     #    print n,common.prettyprintdict(i)
0198         
0199         #print n,i.get('id',""),i.get('n_given', ""),i.get('n_family', "")
0200     
0201 

Generated by PyXR 0.9.4