From 0f1c9ee184a72059adccdd8d197f16017af4888f Mon Sep 17 00:00:00 2001 From: Fredrik Unger Date: Fri, 9 Nov 2012 11:50:33 +0100 Subject: [PATCH] Contact as Person and Organization object, callable using lxml The contact processing was rewrited to objects, using lxml for xml generation. It is also callable from the commandline. One difficulty is with assigning vCard data to the correct object. Ex, a telephone number to an address or person. Currently works well enough to be used in non trivial sites. --- xinclude/contact.py | 314 ++++++++++++++++++++++++-------------------- 1 file changed, 175 insertions(+), 139 deletions(-) diff --git a/xinclude/contact.py b/xinclude/contact.py index e7e0ab3..c6d8034 100755 --- a/xinclude/contact.py +++ b/xinclude/contact.py @@ -1,145 +1,181 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -from vobject import readComponents import sys -from address import Address + +from vobject import readComponents from geohash import encode +from urlparse import urlparse +from getpass import getpass +from lxml import etree +from lxml.builder import ElementMaker -for arg in sys.argv[1:]: - al = arg.split("=") - if al[0] == "lang": - lang = al[1] - if al[0] == "xptr": - argument = al[1] - -(cards,query) = argument.split('?') -(key, name) = query.split(':') -with open(cards, 'r') as f: - card_data = f.read() -f.closed - -# Scan for the correct card -found = None -out = u'' -for card in readComponents(card_data): -# card.prettyPrint() - if key in card.contents.keys(): - if name.decode('utf-8') == card.contents[key][0].value[0]: - found = card - if key == 'firstname': - if name.decode('utf-8') == card.n.value.given: - found = card - if key == 'surname': - if name.decode('utf-8') == card.n.value.family: - found = card - -if not found: - print query+' failed in '+cards - exit(2) -# when card is found parse it to docbook. -pn = '' - -if 'n' in found.contents.keys(): - n = found.n.value - empty = n.prefix == '' and n.given == '' and \ - n.additional =='' and n.family =='' and n.suffix == '' - if not empty: - pn += '' - if n.prefix != '': - pn += ''+n.prefix+' ' - if n.given != '': - pn += ''+n.given+' ' - if n.additional != '': - pn += ''+n.additional+' ' - if n.family != '': - pn += ''+n.family+' ' - if n.suffix != '': - pn += ''+n.suffix+' ' - pn += '' - -ad = '' -if 'adr' in found.contents.keys(): - for a, t in zip(found.contents['adr'],found.contents['tel']): - ad += '\n
' - if a.value.street != '': - ad += ''+a.value.street+'' - if a.value.code != '': - ad += ''+a.value.code+' ' - if a.value.city != '': - ad += ''+a.value.city+'' - if a.value.country != '': - ad += ''+a.value.country+'' - if t.value != '': - ad += ''+t.value+'' - ad += '
' - geostr = u''+a.value.street+', '+a.value.city+', '+a.value.country - (lat,lon) = geocode(geostr.encode('utf-8')) - mapimage([(float(lat),float(lon))]) - ad += maplink(lat,lon) - -o = '' -if 'org' in found.contents.keys(): - o = ''' - - '''+found.org.value[0]+''' - '''+ad+''' - ''' - - -url = '' -if 'url' in found.contents.keys(): - url += ' ' - -geo = '' -#if 'geo' in found.contents.keys(): -# (lat,lon) = found.geo.value.split(';') -# mapimage([(float(lat),float(lon))]) -# # create picture -# geo += maplink(lat,lon) - - -# Turn off email for now -email = '' -#if 'email' in found.contents.keys(): -# email += ''+found.email.value+'' - -if empty: - content = pn+o+url+geo+email -else: - if o == '': - content = ''+pn+ad+url+geo+email+'' - else: - content = ''+pn+''+o+''+url+geo+email+ \ - '' -out = ''' - - '''+content+''' - -''' - -ex =''' -
- Street - Postcode City - Country - +1 123 456 789 -
- - Occupation - - Organization -
- Street - Postcode City - Country - +1 123 456 789 -
-
-
- - -''' - -sys.stdout.write(out.encode('utf-8')) +from address import Address +from treecutter import constants as const + +class Contacts(object): + def __init__(self, uri): + self.uri = uri + self.card_data = None + if uri.scheme == 'file': + with open(self.uri.path, 'r') as f: + self.card_data = f.read() + f.closed + if uri.scheme == 'http': + pw = getpass() + print "http not yet implemented" + if uri.scheme == 'https': + print "https not yet implemented" + + def filter(self,query): + (key, name) = query.split(':') + card = None + for c in readComponents(self.card_data): + if key in c.contents.keys(): + if name.decode('utf-8') == c.contents[key][0].value[0]: + card = c + if key == 'firstname': + if name.decode('utf-8') == c.n.value.given: + card = c + if key == 'surname': + if name.decode('utf-8') == c.n.value.family: + card = c + return Contact(card) + +class Contact(object): + def __init__(self,card): + self.card = card + self.person = None + self.organization = Organization('') + self.address = [] + self.phone = '' + self.jobtitle = '' + self.url = '' + self.email = '' + self.parsecard() + + def parsecard(self): + if 'n' in self.card.contents.keys(): + n = self.card.n.value + empty = n.prefix == '' and n.given == '' and \ + n.additional =='' and n.family =='' and n.suffix == '' + if not empty: + self.person = Person(n.given,n.family, + n.prefix,n.additional,n.suffix) + if 'title' in self.card.contents.keys(): + self.jobtitle = self.card.title.value + + if 'org' in self.card.contents.keys(): + self.organization = Organization(self.card.org.value[0]) + + for av in self.card.contents['adr']: + a = av.value + addr = Address(a.street,a.code,a.city,a.country) + if av.type_param == 'work': + self.organization.add_address(addr) + if av.type_param == 'home': + self.address.append(addr) + addr.geocode() + + for i,t in enumerate(self.card.contents['tel']): + if av.type_param == 'cell': # Does not exist ? + self.phone = t.value + if av.type_param == 'work': + self.organization.add_phone(i,t.value) + if av.type_param == 'home': + self.address[i].add_phone(t.value) + + if 'url' in self.card.contents.keys(): + self.url = self.card.url.value + +# if 'email' in self.card.contents.keys(): +# self.email = self.card.email.value + + + def db_xml(self): + db = ElementMaker(namespace=const.DB_NS, nsmap=const.NSMAP) + if self.person: + pers = db.person(self.person.db_xml(),db.phone(self.phone)) + for a in self.address: + pers.append(a.db_xml()) + pers.append(db.affiliation(db.jobtitle(self.jobtitle), + self.organization.db_xml())) + pers.append(db.email(self.email)) + else: + pers = self.organization.db_xml() + pers.append(db.uri(db.link(self.url,**{const.XLINK+"href": self.url}), + type='website')) + return pers + +class Person(object): + def __init__(self,firstname,surname,honorific,othername,linage): + self.honorific = honorific + self.firstname = firstname + self.othername = othername + self.surname = surname + self.linage = linage + + def db_xml(self): + db = ElementMaker(namespace=const.DB_NS, nsmap=const.NSMAP) + p = db.personname( + db.honorific(self.honorific), + db.firstname(self.firstname), + db.othername(self.othername), + db.surname(self.surname), + db.linage(self.linage) + ) + return p + +class Organization(object): + def __init__(self,orgname): + self.orgname = orgname + self.address = [] + + def add_address(self,addr): + addr.set_name(self.orgname) + self.address.append(addr) + + def add_phone(self, i, phone): + # Quick fix for when an address has two phone numbers : FIXME + try: + self.address[i].add_phone(phone) + except IndexError, e: + pass + + def db_xml(self): + db = ElementMaker(namespace=const.DB_NS, nsmap=const.NSMAP) + org = db.org(db.orgname(self.orgname)) + for a in self.address: + org.append(a.db_xml()) + return org + +def recursively_empty(e): + if e.text or e.tag == const.DB+'imagedata': + return False + return all((recursively_empty(c) for c in e.iterchildren())) + +def clean_db(xml): + context = etree.iterwalk(xml) + for action, elem in context: + parent = elem.getparent() + if recursively_empty(elem): + parent.remove(elem) + + +if __name__ == "__main__": + for arg in sys.argv[1:]: + al = arg.split("=",1) + if al[0] == "lang": + lang = al[1] + if al[0] == "xptr": + argument = al[1] + + (uristr,query) = argument.split('|') + uri = urlparse(uristr) + contact = Contacts(uri).filter(query) + cxml = contact.db_xml() + clean_db(cxml) + + #print(etree.tostring(cxml, pretty_print=True)) + #sys.stdout.write(out.encode('utf-8')) + sys.stdout.write(etree.tostring(cxml,encoding='UTF-8',pretty_print=False)) -- 2.30.2