Contact as Person and Organization object, callable using lxml
authorFredrik Unger <fred@tree.se>
Fri, 9 Nov 2012 10:50:33 +0000 (11:50 +0100)
committerFredrik Unger <fred@tree.se>
Fri, 9 Nov 2012 10:50:33 +0000 (11:50 +0100)
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

index e7e0ab32820a0127f7344fd8b88b310cde60e5c1..c6d8034344fb903216239eed4c889f7bded73e02 100755 (executable)
 #!/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 += '<personname>'
-    if n.prefix != '':
-      pn += '<honorific>'+n.prefix+'</honorific> '
-    if n.given != '':
-      pn += '<firstname>'+n.given+'</firstname> '
-    if n.additional != '':
-      pn += '<othername>'+n.additional+'</othername> '
-    if n.family != '':
-      pn += '<surname>'+n.family+'</surname> '
-    if n.suffix != '':
-      pn += '<lineage>'+n.suffix+'</lineage> '
-    pn += '</personname>'
-
-ad = ''
-if 'adr' in found.contents.keys():
-  for a, t in zip(found.contents['adr'],found.contents['tel']):
-    ad += '\n<address type="'+a.type_param+'"> '
-    if a.value.street != '':
-      ad += '<street>'+a.value.street+'</street>'
-    if a.value.code != '':
-      ad += '<postcode>'+a.value.code+'</postcode> '
-    if a.value.city != '':
-      ad += '<city>'+a.value.city+'</city>'
-    if a.value.country != '':
-      ad += '<country>'+a.value.country+'</country>'
-    if t.value != '':
-      ad += '<phone>'+t.value+'</phone>'
-    ad += '</address>'
-    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 = '''
-      <org>
-        <orgname>'''+found.org.value[0]+'''</orgname>
-        '''+ad+'''
-        </org>'''
-
-
-url = ''
-if 'url' in found.contents.keys():
-  url += '<uri type="website"><link xlink:href="'+found.url.value+'"/></uri> '
-
-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 += '<email>'+found.email.value+'</email>'
-
-if empty:
-  content = pn+o+url+geo+email
-else:
-  if o == '':
-    content = '<person>'+pn+ad+url+geo+email+'</person>'
-  else:
-    content = '<person>'+pn+'<affiliation>'+o+'</affiliation>'+url+geo+email+ \
-        '</person>'
-out = '''
-<para xmlns="http://docbook.org/ns/docbook"
-      xmlns:xlink="http://www.w3.org/1999/xlink">
-  '''+content+'''
-</para>
-'''
-
-ex ='''
-    <address>
-      <street>Street</street>
-      <postcode>Postcode</postcode> <city>City</city>
-      <country>Country</country>
-      <phone>+1 123 456 789</phone>
-    </address>
-    <affiliation>
-      <jobtitle>Occupation</jobtitle>
-      <org>
-        <orgname>Organization</orgname>
-        <address>
-          <street>Street</street>
-          <postcode>Postcode</postcode> <city>City</city>
-          <country>Country</country>
-          <phone>+1 123 456 789</phone>
-        </address>
-      </org>
-    </affiliation>
-  </person>
-</para>
-'''
-
-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))