enhance formatters

This commit is contained in:
Christophe Benz 2010-07-11 22:00:18 +02:00
commit 5547e14e11
8 changed files with 196 additions and 65 deletions

View file

@ -50,6 +50,6 @@ class Videoob(ConsoleApplication):
@ConsoleApplication.command('Search for videos') @ConsoleApplication.command('Search for videos')
def command_search(self, pattern=None): def command_search(self, pattern=None):
self.load_modules(ICapVideo) self.load_modules(ICapVideo)
self.set_header(u'Search pattern: %s' % pattern if pattern else u'Last videos') self.set_formatter_header(u'Search pattern: %s' % pattern if pattern else u'Last videos')
for backend, video in self.do('iter_search_results', pattern=pattern, nsfw=self.options.nsfw): for backend, video in self.do('iter_search_results', pattern=pattern, nsfw=self.options.nsfw):
self.format(video, backend.name) self.format(video, backend.name)

View file

@ -15,6 +15,7 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
import ConfigParser import ConfigParser
import logging import logging
import os import os
@ -22,6 +23,7 @@ import subprocess
import re import re
from weboob.tools.application.console import ConsoleApplication from weboob.tools.application.console import ConsoleApplication
from weboob.tools.ordereddict import OrderedDict
__all__ = ['WeboobCfg'] __all__ = ['WeboobCfg']
@ -30,7 +32,7 @@ __all__ = ['WeboobCfg']
class WeboobCfg(ConsoleApplication): class WeboobCfg(ConsoleApplication):
APPNAME = 'weboobcfg' APPNAME = 'weboobcfg'
VERSION = '0.1' VERSION = '0.1'
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon' COPYRIGHT = 'Copyright(C) 2010 Christophe Benz, Romain Bignon'
def main(self, argv): def main(self, argv):
return self.process_command(*argv[1:]) return self.process_command(*argv[1:])
@ -42,22 +44,18 @@ class WeboobCfg(ConsoleApplication):
return False return False
return True return True
@ConsoleApplication.command('List modules') @ConsoleApplication.command('List backends')
def command_modules(self, *caps): def command_backends(self, *caps):
print ' Name Capabilities Description ' self.set_default_formatter('table')
print '+--------------+----------------------+----------------------------------------+'
self.weboob.modules_loader.load() self.weboob.modules_loader.load()
for name, module in self.weboob.modules_loader.modules.iteritems(): for name, backend in self.weboob.modules_loader.modules.iteritems():
if caps and not self.caps_included(module.iter_caps(), caps): if caps and not self.caps_included(backend.iter_caps(), caps):
continue continue
row = OrderedDict([('Name', name),
first_line = True ('Capabilities', ', '.join(cap.__name__ for cap in backend.iter_caps())),
for cap in module.iter_caps(): ('Description', backend.get_description()),
if first_line: ])
print ' %-14s %-21s %s' % (name, cap.__name__, module.get_description()) self.format(row)
first_line = False
else:
print ' %s' % cap.__name__
@ConsoleApplication.command('List applications') @ConsoleApplication.command('List applications')
def command_applications(self, *caps): def command_applications(self, *caps):
@ -71,24 +69,24 @@ class WeboobCfg(ConsoleApplication):
applications.add(m.group(1)) applications.add(m.group(1))
print ' '.join(sorted(applications)).encode('utf-8') print ' '.join(sorted(applications)).encode('utf-8')
@ConsoleApplication.command('Display a module') @ConsoleApplication.command('Display information about a backend')
def command_modinfo(self, name): def command_info(self, name):
try: try:
module = self.weboob.modules_loader.get_or_load_module(name) backend = self.weboob.modules_loader.get_or_load_module(name)
except KeyError: except KeyError:
logging.error('No such module: %s' % name) logging.error('No such backend: "%s"' % name)
return 1 return 1
print '.------------------------------------------------------------------------------.' print '.------------------------------------------------------------------------------.'
print '| Module %-69s |' % module.get_name() print '| Backend %-68s |' % backend.get_name()
print "+-----------------.------------------------------------------------------------'" print "+-----------------.------------------------------------------------------------'"
print '| Version | %s' % module.get_version() print '| Version | %s' % backend.get_version()
print '| Maintainer | %s' % module.get_maintainer() print '| Maintainer | %s' % backend.get_maintainer()
print '| License | %s' % module.get_license() print '| License | %s' % backend.get_license()
print '| Description | %s' % module.get_description() print '| Description | %s' % backend.get_description()
print '| Capabilities | %s' % ', '.join([cap.__name__ for cap in module.iter_caps()]) print '| Capabilities | %s' % ', '.join([cap.__name__ for cap in backend.iter_caps()])
first = True first = True
for key, field in module.get_config().iteritems(): for key, field in backend.get_config().iteritems():
value = field.description value = field.description
if not field.default is None: if not field.default is None:
value += ' (default: %s)' % field.default value += ' (default: %s)' % field.default
@ -98,7 +96,7 @@ class WeboobCfg(ConsoleApplication):
first = False first = False
else: else:
print '| | %s: %s' % (key, value) print '| | %s: %s' % (key, value)
print "'-----------------' " print "'-----------------'"
@ConsoleApplication.command('Add a configured backend') @ConsoleApplication.command('Add a configured backend')
@ -157,19 +155,22 @@ class WeboobCfg(ConsoleApplication):
except ConfigParser.DuplicateSectionError: except ConfigParser.DuplicateSectionError:
print u'Instance "%s" already exists for backend "%s".' % (new_name, name) print u'Instance "%s" already exists for backend "%s".' % (new_name, name)
@ConsoleApplication.command('List backends') @ConsoleApplication.command('List configured backends')
def command_list(self): def command_configured(self):
print ' Instance Name Name Params ' self.set_default_formatter('table')
print '+---------------+--------------+------------------------------------------------+'
for instance_name, name, params in self.weboob.backends_config.iter_backends(): for instance_name, name, params in self.weboob.backends_config.iter_backends():
print ' %-15s %-14s %-47s' % (instance_name, name, ', '.join('%s=%s' % (key, value) for key, value in params.iteritems())) row = OrderedDict([('Instance name', instance_name),
('Backend name', name),
('Configuration', ', '.join('%s=%s' % (key, value) for key, value in params.iteritems())),
])
self.format(row)
@ConsoleApplication.command('Remove a backend') @ConsoleApplication.command('Remove a configured backend')
def command_remove(self, instance_name): def command_remove(self, instance_name):
try: try:
self.weboob.backends_config.remove_backend(instance_name) self.weboob.backends_config.remove_backend(instance_name)
except ConfigParser.NoSectionError: except ConfigParser.NoSectionError:
logging.error("Backend '%s' does not exist" % instance_name) logging.error('Backend instance "%s" does not exist' % instance_name)
return 1 return 1
@ConsoleApplication.command('Edit configuration file') @ConsoleApplication.command('Edit configuration file')

View file

@ -66,6 +66,6 @@ class Weboorrents(ConsoleApplication):
@ConsoleApplication.command('Search torrents') @ConsoleApplication.command('Search torrents')
def command_search(self, pattern=None): def command_search(self, pattern=None):
self.set_header(u'Search pattern: %s' % pattern if pattern else u'Last torrents') self.set_formatter_header(u'Search pattern: %s' % pattern if pattern else u'Last torrents')
for backend, torrent in self.weboob.do('iter_torrents', pattern=pattern): for backend, torrent in self.weboob.do('iter_torrents', pattern=pattern):
self.format(torrent, backend.name) self.format(torrent, backend.name)

View file

@ -27,7 +27,7 @@ from weboob.core import CallErrors
from weboob.core.modules import BackendsConfig from weboob.core.modules import BackendsConfig
from .base import BackendNotFound, BaseApplication from .base import BackendNotFound, BaseApplication
from .formatters.instances import formatters from .formatters.load import formatters, load_formatter
from .results import ResultsCondition, ResultsConditionException from .results import ResultsCondition, ResultsConditionException
@ -60,8 +60,7 @@ class ConsoleApplication(BaseApplication):
results_options = OptionGroup(self._parser, 'Results Options') results_options = OptionGroup(self._parser, 'Results Options')
results_options.add_option('-c', '--condition', help='filter result items to display given a boolean condition') results_options.add_option('-c', '--condition', help='filter result items to display given a boolean condition')
results_options.add_option('-f', '--formatter', default='multiline', choices=formatters.keys(), results_options.add_option('-f', '--formatter', choices=formatters, help='select output formatter (%s)' % u','.join(formatters))
help='select output formatter (%s)' % u','.join(formatters.keys()))
results_options.add_option('-s', '--select', help='select result item key(s) to display (comma-separated)') results_options.add_option('-s', '--select', help='select result item key(s) to display (comma-separated)')
self._parser.add_option_group(results_options) self._parser.add_option_group(results_options)
@ -73,7 +72,11 @@ class ConsoleApplication(BaseApplication):
pass pass
def _handle_app_options(self): def _handle_app_options(self):
self.formatter = formatters[self.options.formatter] if self.options.formatter:
formatter_name = self.options.formatter
else:
formatter_name = 'multiline'
self.formatter = load_formatter(formatter_name)
if self.options.no_header: if self.options.no_header:
self.formatter.display_header = False self.formatter.display_header = False
@ -205,7 +208,11 @@ class ConsoleApplication(BaseApplication):
def command(doc_string, f=register_command): def command(doc_string, f=register_command):
return partial(f, doc_string=doc_string) return partial(f, doc_string=doc_string)
def set_header(self, string): def set_default_formatter(self, name):
if not self.options.formatter:
self.formatter = load_formatter(name)
def set_formatter_header(self, string):
self.formatter.set_header(string) self.formatter.set_header(string)
def format(self, result, backend_name=None): def format(self, result, backend_name=None):

View file

@ -43,7 +43,8 @@ class IFormatter(object):
call it. It can be used to specify the fields order. call it. It can be used to specify the fields order.
@param obj [object] object to format @param obj [object] object to format
@param selected_fields [list] fields to display. If None, all fields are selected. @param backend_name [str] name of backend, used to create object ID
@param selected_fields [tuple] fields to display. If None, all fields are selected
@param condition [Condition] condition to objects to display @param condition [Condition] condition to objects to display
@return a string of the formatted object @return a string of the formatted object
""" """

View file

@ -15,30 +15,28 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from .multiline import MultilineFormatter
from .simple import SimpleFormatter __all__ = ['load_formatter']
__all__ = ['formatters'] formatters = ('htmltable', 'multiline', 'simple', 'table', 'webkit')
formatters = dict( def load_formatter(name):
multiline=MultilineFormatter(), if name not in formatters:
simple=SimpleFormatter(), raise Exception(u'Formatter "%s" not found' % name)
) if name in ('htmltable', 'table'):
from .table import TableFormatter
try: if name == 'htmltable':
from .table import TableFormatter return TableFormatter(result_funcname='get_html_string')
formatters.update(dict( elif name == 'table':
table=TableFormatter(), return TableFormatter()
htmltable=TableFormatter(result_funcname='get_html_string'), elif name == 'simple':
)) from .simple import SimpleFormatter
try: return SimpleFormatter()
elif name == 'multiline':
from .multiline import MultilineFormatter
return MultilineFormatter()
elif name == 'webkit':
from .webkit import WebkitGtkFormatter from .webkit import WebkitGtkFormatter
formatters.update(dict( return WebkitGtkFormatter()
webkit=WebkitGtkFormatter(),
))
except ImportError:
pass
except ImportError:
pass

View file

@ -48,7 +48,7 @@ class TableFormatter(IFormatter):
elif self.result_funcname == 'get_html_string': elif self.result_funcname == 'get_html_string':
s+= '<p>%s</p>' % self.header s+= '<p>%s</p>' % self.header
s += "\n" s += "\n"
table = PrettyTable(self.column_headers) table = PrettyTable(list(self.column_headers))
for column_header in self.column_headers: for column_header in self.column_headers:
table.set_field_align(column_header, 'l') table.set_field_align(column_header, 'l')
for line in self.queue: for line in self.queue:

124
weboob/tools/ordereddict.py Normal file
View file

@ -0,0 +1,124 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Christophe Benz
#
# 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, version 3 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
try:
from ordereddict import OrderedDict
except:
## {{{ http://code.activestate.com/recipes/576693/ (r6)
from UserDict import DictMixin
class OrderedDict(dict, DictMixin):
def __init__(self, *args, **kwds):
if len(args) > 1:
raise TypeError('expected at most 1 arguments, got %d' % len(args))
try:
self.__end
except AttributeError:
self.clear()
self.update(*args, **kwds)
def clear(self):
self.__end = end = []
end += [None, end, end] # sentinel node for doubly linked list
self.__map = {} # key --> [key, prev, next]
dict.clear(self)
def __setitem__(self, key, value):
if key not in self:
end = self.__end
curr = end[1]
curr[2] = end[1] = self.__map[key] = [key, curr, end]
dict.__setitem__(self, key, value)
def __delitem__(self, key):
dict.__delitem__(self, key)
key, prev, next = self.__map.pop(key)
prev[2] = next
next[1] = prev
def __iter__(self):
end = self.__end
curr = end[2]
while curr is not end:
yield curr[0]
curr = curr[2]
def __reversed__(self):
end = self.__end
curr = end[1]
while curr is not end:
yield curr[0]
curr = curr[1]
def popitem(self, last=True):
if not self:
raise KeyError('dictionary is empty')
if last:
key = reversed(self).next()
else:
key = iter(self).next()
value = self.pop(key)
return key, value
def __reduce__(self):
items = [[k, self[k]] for k in self]
tmp = self.__map, self.__end
del self.__map, self.__end
inst_dict = vars(self).copy()
self.__map, self.__end = tmp
if inst_dict:
return (self.__class__, (items,), inst_dict)
return self.__class__, (items,)
def keys(self):
return list(self)
setdefault = DictMixin.setdefault
update = DictMixin.update
pop = DictMixin.pop
values = DictMixin.values
items = DictMixin.items
iterkeys = DictMixin.iterkeys
itervalues = DictMixin.itervalues
iteritems = DictMixin.iteritems
def __repr__(self):
if not self:
return '%s()' % (self.__class__.__name__,)
return '%s(%r)' % (self.__class__.__name__, self.items())
def copy(self):
return self.__class__(self)
@classmethod
def fromkeys(cls, iterable, value=None):
d = cls()
for key in iterable:
d[key] = value
return d
def __eq__(self, other):
if isinstance(other, OrderedDict):
return len(self)==len(other) and self.items() == other.items()
return dict.__eq__(self, other)
def __ne__(self, other):
return not self == other
## end of http://code.activestate.com/recipes/576693/ }}}