#!/usr/bin/python """ Import and Export model """ # Copyright (C) 2004 Henning Jacobs # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # $Id: converters.py 82 2004-07-11 13:01:44Z henning $ import vcard import debug import types class EncodedFileWriter: "Takes Unicode Strings and writes them encoded to the real file." def __init__(self, realfile, encoding): self._fd = realfile self._encoding = encoding def write(self, val): if isinstance(val, types.UnicodeType): self._fd.write(val.encode(self._encoding, 'replace')) else: self._fd.write(val) def close(self): self._fd.close() def export2vcard(model, outfile, encoding, cardhandles=None): "Simply fetch raw vCard Data from Server and write to File" if cardhandles is None: cardhandles=model.ListHandles() cardlist = vcard.vCardList() for handle in cardhandles: cardlist.add(model.GetContact(handle)) outfile.write(cardlist.VCF_repr()) def export2xmlrdf(model, outfile, encoding, cardhandles=None): "Export to vCard-RDF (XML) Data" if cardhandles is None: cardhandles=model.ListHandles() cardlist = vcard.vCardList() for handle in cardhandles: cardlist.add(model.GetContact(handle)) outfile.write(cardlist.XML_repr()) def export2csv(model, outfile, encoding, cardhandles=None, fieldnames=vcard.FIELDNAMES): "Export to Comma Separated Values" import csv writer = csv.writer(outfile) # local copy: fieldnames = fieldnames[:] # Hack: for field in ["Phone", "Email"]: idx = fieldnames.index(field) for i in range(3): fieldnames.insert(idx+i+1, "%s %d" % (field, i+2)) writer.writerow(fieldnames) if cardhandles is None: cardhandles=model.ListHandles() for handle in cardhandles: row = [] for field in fieldnames: val = model.GetContact(handle).getFieldValueStr(field).encode(encoding, 'replace') # Newline break things: val = val.replace('\n', r'\n').replace('\r', r'\r') row.append(val) writer.writerow(row) def export2latex(model, outfile, encoding='latin-1', cardhandles=None): "Export to Fancy LaTeX Pages" # Subset of vcard.FIELDNAMES: fields2export = [ "FormattedName", #"DisplayName", #"Family", #"Given", #"Additional", #"Prefixes", #"Suffixes", #"NickName", "Birthday", "Organization", "Units", "Title", "Role", "Phone", "Email", #"Mailer", "POBox", "Extended", "Street", "PostalCode", "City", "Region", "Country", #"Label", "Note", "Categories", #"SortName", #"SortString", "URL", #"Key", "TimeZone", "GlobalPosition", #"Rev", "UID" ] template = r"""\documentclass[10pt]{article} \usepackage[a4paper]{geometry} \usepackage[latin1]{inputenc} \usepackage{color} \definecolor{light-gray}{gray}{0.75} \usepackage{multicol} \usepackage{fancyhdr} \usepackage{times} % Select Helvetica as default font: \renewcommand{\familydefault}{phv} \pagestyle{fancy} \rhead{\tt\scriptsize vCards converted to LaTeX by PyCoCuMa v on } \cfoot{\thepage} \def\begincard#1{ \noindent\begin{minipage}{\linewidth} \parindent = 0mm \dimen1=\linewidth \advance\dimen1 by -2\fboxsep \setbox0=\hbox to \dimen1{\small #1 \hfill} \dp0=0pt \colorbox{light-gray}{\box0} \vskip 1mm \bgroup\scriptsize} \def\endcard{\egroup \end{minipage}\vskip 2mm} \begin{document} \begin{multicols}{3} \begincard{} \endcard \end{multicols} \end{document} """.splitlines(True) def texescape(str): "Escape special TeX characters" return str.replace('&','\&').replace('_','\_').replace('$','\$').replace('#','\#') if cardhandles is None: cardhandles=model.ListHandles() globals = {'ret':'', 'cardidx':0} def nextCard(globals=globals, cardhandles=cardhandles): idx = globals['cardidx'] if idx >= len(cardhandles): return False else: globals['card'] = model.GetContact(cardhandles[idx]) globals['cardidx'] += 1 return True def getFieldValue(str, globals=globals): return globals['card'].getFieldValueStr(str) def printLines(globals=globals): card = globals['card'] ret = '' addresses = [] for field in fields2export: valcount = 0 val = card.getFieldValueStr(field, default=None) while val is not None: valcount += 1 if field in vcard.ADRFIELDS: if len(addresses) < valcount: addresses.append({}) adr = addresses[valcount-1] adr[field] = val if len(adr) >= len(vcard.ADRFIELDS): zeilen = [adr["POBox"], adr["Extended"], adr["Street"], " ".join(filter(None, [adr["PostalCode"], adr["City"]])), ", ".join(filter(None, [adr["Region"], adr["Country"]]))] for z in filter(None, zeilen): ret = ret + " "+ z + "\par\n" elif val: ret = ret + " "+ val + "\par\n" val = card.getFieldValueStr(field, default=None) return ret import __version__ import time globals.update({'Version': __version__.__version__, 'DateTime':time.asctime(), 'NextCard':nextCard, 'GetFieldValue':getFieldValue, 'PrintLines':printLines}) def outfunc(str, outfile=outfile): outfile.write(str) from TemplateProcessor import TemplateProcessor proc = TemplateProcessor(postproc=texescape) proc.process(template, outfunc, globals) def export2pine(model, outfile, encoding, cardhandles=None): "Export to Pine Mail Addressbook" if cardhandles is None: cardhandles=model.ListHandles() # Support up to three defined email-addresses per contact: rows = model.QueryAttributes(cardhandles, ('NickName','DisplayName','Email 1','Email 2','Email 3')) for row in rows: # rows are immutable tuples, make them mutable: row = list(row) if row[2]: # do split()[0] on each email, because there may follow # params: (pref, internet) for example: emails = map(lambda x: x.split()[0], filter(None, row[2:])) row[2] = ', '.join(emails) # More than one Email-Addr, so enclose with brackets: if len(emails)>1: row[2] = "(%s)" % row[2] outfile.write('\t'.join(row[:3]) + '\n') def importFvcard(model, infile, encoding): "Import from VCF-File" importcards = vcard.vCardList() importcards.LoadFromStream(infile, encoding) for cardhdl in importcards.sortedlist(): handle = model.NewContact() model.PutContact(handle, importcards[cardhdl].VCF_repr()) def importFcsv(model, infile, encoding): "Import from CSV-Table" import csv reader = csv.reader(infile) firstrow = None for row in reader: if firstrow is None: firstrow = row unknownfields = filter(lambda x: x[0] not in vcard.FIELDNAMES, zip(firstrow, range(len(firstrow)))) if unknownfields: from FieldMappingDialog import FieldMappingDialog dlg = FieldMappingDialog(None, [field for field, no in unknownfields], title='Map Fields', headline='Please map the unknown fields to vCard fields.\n'+ 'Unmapped fields will be ignored.') dlg.activate() for mappedto, i in zip(dlg.getvalue(), [no for field, no in unknownfields]): firstrow[i] = mappedto else: handle = model.NewContact() try: card = model.GetContact(handle) for i in range(len(row)): # firstrow must have at least len(row) elements: if row[i] and firstrow[i]: card.setFieldValueStr(firstrow[i], unicode(row[i], encoding, 'replace')) model.PutContact(handle, card.VCF_repr()) except: # Something bad happened, don't leave our DB inconsistent: model.DelContact(handle) raise exportfunctions = { "vcard":export2vcard, "xmlrdf":export2xmlrdf, "csv": export2csv, "latex":export2latex, "pine": export2pine} def exportvcards(model, targetformat, outfile, encoding, cardhandles=None): "Chooses the right exportfunction according to targetformat" func = exportfunctions.get(targetformat) if func: return func(model, outfile, encoding, cardhandles) else: return None exportfiletypes = { "vcard":("vCard", "*.vcf"), "xmlrdf":("XML Files", "*.xml"), "csv": ("Comma Separated Text", "*.csv"), "latex":("LaTeX Document", "*.tex"), "pine": ("Pine Addressbook", "*")} def askexportfile(targetformat, master=None): "Ask for Export Filename" import tkFileDialog, os try: dir = os.getcwd() except: dir = "" dlg = tkFileDialog.SaveAs(master, filetypes=[exportfiletypes[targetformat]]) fname = dlg.show(initialdir=dir, initialfile="") if fname: root, ext = os.path.splitext(fname) if root and not ext: ext = exportfiletypes[targetformat][1][1:] return root+ext else: return None importfunctions = { "vcard":importFvcard, "csv": importFcsv} def importvcards(model, srcformat, infile, encoding): "Chooses the right importfunction according to srcformat" func = importfunctions.get(srcformat) if func: return func(model, infile, encoding) else: return None importfiletypes = { "vcard":("vCard", "*.vcf"), "csv": ("Comma Separated Text", "*.csv")} def askimportfile(srcformat, master=None): "Ask for Import Filename" import tkFileDialog, os try: dir = os.getcwd() except: dir = "" dlg = tkFileDialog.Open(master, filetypes=[importfiletypes[srcformat], ("All Files", "*")], initialdir=dir, initialfile="") return dlg.show() importformats = [ ("vcard","vCard 3.0 File (*.vcf)"), ("csv", "Comma Separated Values (*.csv)")] def Import(master, model, srcformat=None): "Show Import Dialog and do importing" if srcformat is None: from ImportExportDialog import ImportExportDialog dlg = ImportExportDialog(master, importformats, title="Import Contacts", headline="Import from:") res = dlg.activate() if res == 'Ok': dlg.deactivate() srcformat, srcencoding = dlg.getvalue() else: return filename = askimportfile(srcformat, master) if filename: fd = file(filename, 'rb') importvcards(model, srcformat, fd, srcencoding) fd.close() return True else: return False exporttargets = [ ("vcard","vCard 3.0 File (*.vcf)"), ("xmlrdf","XML-RDF (*.xml)"), ("csv", "Comma Separated Values (*.csv), MSExcel compatible"), ("latex","LaTeX Document (*.tex)"), ("pine", "Pine Addressbook")] def Export(master, model, targetformat=None, cardhandles=None): "Show Export Dialog and do exporting" if targetformat is None: from ImportExportDialog import ImportExportDialog if cardhandles and len(cardhandles) == 1: title = "Export Single Contact" else: title = "Export Contacts" dlg = ImportExportDialog(master, exporttargets, title=title, headline="Export to:") res = dlg.activate() if res == 'Ok': dlg.deactivate() targetformat, targetencoding = dlg.getvalue() else: return # For py2exe ModuleFinder: import codecs from encodings import latin_1 filename = askexportfile(targetformat, master) if filename: fd = open(filename, "wb") outfile = EncodedFileWriter(fd, targetencoding) exportvcards(model, targetformat, outfile, targetencoding, cardhandles) fd.close()