nutools/lib/pyulib/src/uapps/pyucontacts.py

329 lines
11 KiB
Python
Executable File

#!/usr/bin/env python
# -*- coding: utf-8 mode: python -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
u"""%(scriptname)s: afficher les contacts téléphoniques
USAGE
%(scriptname)s [patterns...]"""
import os, sys, re, string
from os import path
from ulib.all import *
class ContactsrcFile(ShConfigFile):
CONTACTSRC = "contactsrc"
DEFAULT_CONTACTSFILE = "Contacts.txt"
def __init__(self, file=None, raise_exception=True):
if file is None:
testdir = path.join(scriptdir, "../../test")
utoolsrcdir = path.join(path.expanduser("~"), ".utools")
if path.isdir(testdir): file = path.join(testdir, self.CONTACTSRC)
else: file = path.join(utoolsrcdir, self.CONTACTSRC)
raise_exception = False
ShConfigFile.__init__(self, file=file, raise_exception=raise_exception)
CONTACTSDIR = "contactsdir"
CONTACTSFILE = "contacts"
def __p(self, p, refdir=None):
if refdir is not None:
if path.isfile(refdir): refdir = path.split(refdir)[0]
return abspath2(path.expanduser(p), refdir)
def __pcd(self, p):
return self.__p(p, self.get_contactsdir())
def __get(self, name, default, list=False, strip=False, fix_path=True):
if fix_path is True: fix_path = self.__pcd
if self.has_key(name):
if list:
values = self.get_list(name, strip)
if fix_path: values = [fix_path(value) for value in values]
value = values
else:
value = self[name]
if fix_path: value = fix_path(value)
else:
value = default
return value
def __join(self, values):
if isseq(values): values = u"\n".join(map(lambda v: _u(v), values))
else: values = _u(values)
return values
def __set(self, name, value, list=False):
if value is None:
if self.has_key(name): del self[name]
else:
self[name] = self.__join(value)
def get_contactsdir(self):
"""Retourner le répertoire de base pour les fichiers de contact
Par défaut, il s'agit de ~/.utools
"""
return self.__get(self.CONTACTSDIR, self.absfiledir, list=True, strip=True, fix_path=self.__p)
def set_contactsdir(self, contactsdir=None):
self.__set(self.CONTACTSDIR, contactsdir, list=True)
def get_default_contactsfile(self):
return self.__pcd(self.DEFAULT_CONTACTSFILE)
def get_contactsfile(self, default=True):
"""Retourner la valeur contactsfile si elle est définie, default sinon.
Il s'agit du fichier qui contient les tâches par défaut.
Si default==True, retourner la première valeur de get_stores(), sinon
la valeur par défaut ~/.utools/Contacts.txt
"""
if default is True:
default = self.get_default_contactsfile()
return self.__get(self.CONTACTSFILE, default)
def set_contactsfile(self, contactsfile):
self.__set(self.CONTACTSFILE, contactsfile)
def compile_pattern(pattern):
if isstr(pattern): pattern = re.compile(pattern)
return pattern
TEL = u"Tél personnel"
FAX = u"Fax personnel"
GSM = u"GSM personnel"
EMAIL = u"E-mail"
ADRESSE = u"Adresse personnelle"
PROFTEL = u"Tél professionnel"
PROFFAX = u"Fax professionnel"
PROFGSM = u"GSM Professionnel"
PROFEMAIL = u"E-mail professionnel"
PROFADRESSE = u"Adresse professionnelle"
class Telephone(object):
_tel = None
_prefix = None
RE_SPACES = re.compile(r'\s+')
PREFIXES = {None: TEL, u"tel": TEL, u"fax": FAX, u"gsm": GSM,
u"ptel": PROFTEL, u"pfax": PROFFAX, u"pgsm": PROFGSM,
u"email": EMAIL, u"mail": EMAIL,
u"pemail": PROFEMAIL, u"pmail": PROFEMAIL,
u"adresse": ADRESSE, u"padresse": PROFADRESSE,
u"adr": ADRESSE, u"padr": PROFADRESSE,
}
LITERALS = [EMAIL, PROFEMAIL, ADRESSE, PROFADRESSE]
def is_literal(cls, prefix):
prefix = cls.PREFIXES.get(prefix, prefix)
return prefix in cls.LITERALS
is_literal = classmethod(is_literal)
def __init__(self, tel, prefix=None):
tel = self.RE_SPACES.sub(u"", tel)
prefix = self.PREFIXES.get(prefix, prefix)
self._tel = _u(tel)
self._prefix = _u(prefix)
prefix = property(lambda self: self._prefix)
def get_tel(self, cooked=True):
if not cooked or self._prefix in self.LITERALS:
return self._tel
else:
tel = self._tel
nums = []
while tel:
if len(tel) > 4:
num = tel[-2:]
tel = tel[:-2]
nums.insert(0, num)
else:
nums.insert(0, tel)
tel = None
return u" ".join(nums)
tel = property(get_tel)
def __str__(self):
s = _s(self.tel)
if self._prefix is not None:
s = "%s (%s)" % (s, _s(self._prefix))
return s
def __repr__(self):
tel = self.tel
prefix = self._prefix
if prefix is None: prefix = "None"
else: prefix = "'%s'" % prefix
return "%s('%s', %s)" % (self.__class__.__name__, tel, prefix)
def __unicode__(self):
u = _u(self.tel)
if self._prefix is not None:
u = u"%s (%s)" % (u, _u(self._prefix))
return u
def matches(self, pattern):
return compile_pattern(pattern).search(self._tel) is not None
class Contact(object):
_nom = None
_prenom = None
_tels = None
RE_COMMA = re.compile(r'\s*,\s*')
RE_PREF = re.compile(r'(\S+)=')
RE_PREF_TEL = re.compile(r'(?:(\S+)=)?((?:\d|\s)+)$')
RE_PREF_ANY = re.compile(r'(?:(\S+)=)?\s*(.+)\s*$')
def __init__(self, line):
parts = self.RE_COMMA.split(line.strip())
self._nom = parts[0:1] and parts[0] or u""
self._prenom = parts[1:2] and parts[1] or u""
self._tels = []
for part in filter(None, parts[2:]):
if not part: continue
mo = self.RE_PREF.match(part)
if mo is not None:
prefix = mo.group(1)
if Telephone.is_literal(prefix):
mo = self.RE_PREF_ANY.match(part)
if mo is None: raise ValueError("inconsistent match: %s" % part)
prefix, tel = mo.group(1), mo.group(2)
self._tels.append(Telephone(tel, prefix))
continue
mo = self.RE_PREF_TEL.match(part)
if mo is None:
raise ValueError("Invalid telephone number: %s" % part)
prefix, tel = mo.group(1), mo.group(2)
self._tels.append(Telephone(tel, prefix))
nom = property(lambda self: self._nom)
prenom = property(lambda self: self._prenom)
tels = property(lambda self: self._tels)
def get_np(self):
np = []
if self._nom: np.append(self._nom)
if self._prenom: np.append(self._prenom)
return u" ".join(np)
np = property(get_np)
def get_pn(self):
pn = []
if self._prenom: pn.append(self._prenom)
if self._nom: pn.append(self._nom)
return u" ".join(pn)
pn = property(get_pn)
def _tel(self, name, cooked=True):
name = Telephone.PREFIXES.get(name, name)
for tel in self.tels:
if tel.prefix == name:
return tel.get_tel(cooked)
return None
tel = property(lambda self: self._tel("tel"))
fax = property(lambda self: self._tel("fax"))
gsm = property(lambda self: self._tel("gsm"))
mail = property(lambda self: self._tel("mail"))
ptel = property(lambda self: self._tel("ptel"))
pfax = property(lambda self: self._tel("pfax"))
pgsm = property(lambda self: self._tel("pgsm"))
pmail = property(lambda self: self._tel("pmail"))
xtel = property(lambda self: self._tel("tel", False))
xfax = property(lambda self: self._tel("fax", False))
xgsm = property(lambda self: self._tel("gsm", False))
xmail = property(lambda self: self._tel("mail", False))
xptel = property(lambda self: self._tel("ptel", False))
xpfax = property(lambda self: self._tel("pfax", False))
xpgsm = property(lambda self: self._tel("pgsm", False))
xpmail = property(lambda self: self._tel("pmail", False))
def matches(self, patterns):
if not isseq(patterns): patterns = (patterns,)
patterns = map(compile_pattern, patterns)
nom = self._nom
prenom = self._prenom
tels = self._tels
for pattern in patterns:
if pattern.search(nom) is None:
if pattern.search(prenom) is None:
for tel in tels:
if tel.matches(pattern): return True
return False
return True
RE_COMMENT = re.compile(r'^#|^\s*$')
def load_contacts(infdn):
lines = Lines()
lines.readlines(infdn)
lines.filter(lambda line: RE_COMMENT.match(line) is None)
return [Contact(line) for line in lines]
def show_contacts(contacts):
for contact in contacts:
etitle(contact.np)
for tel in contact.tels:
uprint(tel)
def display_help():
uprint(__doc__ % globals())
def run_Contacts():
options, longoptions = build_options([
('h', 'help', "Afficher l'aide"),
('o:', 'output=', "Exporter au format csv pour Outlook"),
])
options, args = get_args(None, options, longoptions)
action = "search"
outf = None
for option, value in options:
if option in ('-h', '--help'):
display_help()
sys.exit(0)
elif option in ('-o', '--output'):
action = "export"
outf = value
cf = ContactsrcFile()
contacts = load_contacts(cf.get_contactsfile())
if action == "search":
if args:
patterns = map(compile_pattern, map(_u, args))
contacts = [contact for contact in contacts
if contact.matches(patterns)]
show_contacts(contacts)
elif action == "export":
lines = []
lines.append(u'"Nom","Téléphone (domicile)","Télécopie (domicile)","Tél. mobile","Adresse de messagerie","Téléphone (bureau)","Télécopie (bureau)","Téléphone 2 (bureau)","Adresse de messagerie 2"\r\n')
for contact in contacts:
values = [contact.pn or u"",
contact.xtel or u"",
contact.xfax or u"",
contact.xgsm or u"",
contact.xmail or u"",
contact.xptel or u"",
contact.xpfax or u"",
contact.xpgsm or u"",
contact.xpmail or u"",
]
if values[7]:
if not values[5]:
values[5] = values[7]
values[7] = u""
elif not values[3]:
values[3] = values[7]
values[7] = u""
lines.append(u",".join(values) + u"\r\n")
cp1252 = UnicodeIO("cp1252")
lines = map(cp1252.s, lines)
close = True
if outf != "-":
outf = open(outf, "wb")
else:
outf = sys.stdout
close = False
outf.writelines(lines)
if close: outf.close()
if __name__ == '__main__':
run_Contacts()