#!/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()