#!/usr/bin/python # # Copyright 2013 Lars Wirzenius # # 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 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import cliapp import logging import os import sys import yaml class Entry(object): def __init__(self, parsed_yaml): assert type(parsed_yaml) is dict, repr(parsed_yaml) self._dict = parsed_yaml def as_yaml(self): return yaml.safe_dump(self._dict, default_flow_style=False) def get_single(self, key, default): names = self.get_subdict(key) if not names: return default keys = sorted(names.keys()) return names[keys[0]] def get_subdict(self, key): if key in self._dict: v = self._dict[key] if type(v) is dict: return v return { '': v } return {} class AddressBook(object): def __init__(self): self.entries = [] def find_yaml_files(self, database_root): for dirname, subdirs, basenames in os.walk(database_root): subdirs.sort() for basename in sorted(basenames): if basename.endswith('.yaml'): yield os.path.join(dirname, basename) def add_from_database(self, database_root): logging.info('Adding from database %s' % database_root) for yaml_filename in self.find_yaml_files(database_root): self.add_from_file(yaml_filename) def add_from_file(self, filename): logging.info('Adding from file %s' % filename) with open(filename) as f: parsed_yaml = yaml.safe_load(f) entry = Entry(parsed_yaml) self.entries.append(entry) def find(self, patterns): return [e for e in self.entries if self.matches(e, patterns)] def matches(self, entry, patterns): s = entry.as_yaml().lower() return any(p.lower() in s for p in patterns) class CommandLineAddressBook(cliapp.Application): def add_settings(self): self.settings.string_list( ['database', 'db', 'd'], 'add DIR to list of databases to use', metavar='DIR', default=[os.path.expanduser('~/.local/share/clab')]) def load_address_book(self): book = AddressBook() for database in self.settings['database']: book.add_from_database(database) return book def cmd_list(self, args): book = self.load_address_book() for entry in book.entries: self.output.write(entry.as_yaml() + '\n') def cmd_find(self, args): book = self.load_address_book() for entry in book.find(args): self.output.write(entry.as_yaml() + '\n') def cmd_mutt_query(self, args): if len(args) != 1: raise cliapp.AppException( 'mutt-query requires exactly one argument') book = self.load_address_book() entries = book.find(args) if not entries: self.output.write('No matches\n') sys.exit(1) self.output.write('clab found matches:\n') for entry in entries: name = entry.get_single('name', '') emails = entry.get_subdict('email') for email in emails: n = name.encode('utf-8') self.output.write('%s\t%s\n' % (emails[email], n)) CommandLineAddressBook().run()