capabilities objets inherit from CapBaseObject (refs #369)

This commit is contained in:
Romain Bignon 2010-08-12 17:22:04 +02:00
commit e980e040ba
20 changed files with 126 additions and 102 deletions

View file

@ -35,14 +35,14 @@ class Boobank(ConsoleApplication):
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
def main(self, argv):
self.load_configured_backends(ICapBank)
return self.process_command(*argv[1:])
@ConsoleApplication.command('List every available accounts')
def command_list(self):
self.load_configured_backends(ICapBank)
try:
for backend, account in self.do('iter_accounts'):
self.format(account, backend.name)
self.format(account)
except weboob.core.CallErrors, errors:
for backend, error, backtrace in errors:
if isinstance(error, weboob.tools.browser.BrowserIncorrectPassword):
@ -52,19 +52,13 @@ class Boobank(ConsoleApplication):
@ConsoleApplication.command('Display all future operations')
def command_coming(self, id):
total = 0.0
id, backend_name = self.parse_id(id)
names = (backend_name,) if backend_name is not None else None
self.load_configured_backends(ICapBank, names=names)
def do(backend):
account = backend.get_account(id)
return backend.iter_operations(account)
try:
for backend, operation in self.do(do):
self.format(operation, backend.name)
total += operation.amount
except weboob.core.CallErrors, errors:
for backend, error, backtrace in errors:
if isinstance(error, AccountNotFound):
logging.error(u'Error: account %s not found' % id)
else:
logging.error(u'Error[%s]: %s\n%s' % (backend.name, error, backtrace))
for backend, operation in self.do(do):
self.format(operation)

View file

@ -47,12 +47,12 @@ class Chatoob(ConsoleApplication):
@ConsoleApplication.command('list online contacts')
def command_list(self):
for backend, contact in self.do('iter_contacts', status=Contact.STATUS_ONLINE, caps=ICapContact):
self.format(contact, backend.name)
self.format(contact)
@ConsoleApplication.command('get messages')
def command_messages(self):
for backend, message in self.do('iter_chat_messages'):
self.format(message, backend.name)
self.format(message)
@ConsoleApplication.command('send message to contact')
def command_send(self, _id, message):

View file

@ -37,6 +37,6 @@ class Geolooc(ConsoleApplication):
self.load_configured_backends(ICapGeolocIp)
for backend, location in self.do('get_location', argv[1]):
self.format(location, backend.name)
self.format(location)
return 0

View file

@ -37,7 +37,7 @@ class Traveloob(ConsoleApplication):
def command_stations(self, pattern):
self.load_backends(ICapTravel)
for backend, station in self.do('iter_station_search', pattern):
self.format(station, backend.name)
self.format(station)
@ConsoleApplication.command('List all departures for a given station')
def command_departures(self, station, arrival=None):
@ -59,4 +59,4 @@ class Traveloob(ConsoleApplication):
self.load_backends(ICapTravel, names=backends)
for backend, departure in self.do('iter_station_departures', station_id, arrival_id):
self.format(departure, backend.name)
self.format(departure)

View file

@ -45,7 +45,7 @@ class Videoob(ConsoleApplication):
for backend, video in self.do('get_video', _id):
if video is None:
continue
self.format(video, backend.name)
self.format(video)
@ConsoleApplication.command('Search for videos')
def command_search(self, pattern=None):
@ -53,4 +53,4 @@ class Videoob(ConsoleApplication):
self.set_formatter_header(u'Search pattern: %s' % pattern if pattern else u'Latest videos')
for backend, video in self.do('iter_search_results', pattern=pattern, nsfw=self.options.nsfw,
max_results=self.options.count):
self.format(video, backend.name)
self.format(video)

View file

@ -47,6 +47,6 @@ class WeboobCli(ConsoleApplication):
self.load_backends(cap_s)
for backend, obj in self.do(cmd, *args):
self.format(obj, backend.name)
self.format(obj)
return 0

View file

@ -42,7 +42,7 @@ class Weboorrents(ConsoleApplication):
found = 0
for backend, torrent in self.do('get_torrent', _id, backends=backend_name):
if torrent:
self.format(torrent, backend.name)
self.format(torrent)
found = 1
if not found:
@ -67,4 +67,4 @@ class Weboorrents(ConsoleApplication):
def command_search(self, pattern=None):
self.set_formatter_header(u'Search pattern: %s' % pattern if pattern else u'Latest torrents')
for backend, torrent in self.do('iter_torrents', pattern=pattern):
self.format(torrent, backend.name)
self.format(torrent)

View file

@ -39,13 +39,13 @@ class WetBoobs(ConsoleApplication):
@ConsoleApplication.command('search cities')
def command_search(self, pattern):
for backend, city in self.do('iter_city_search', pattern):
self.format(city, backend.name)
self.format(city)
@ConsoleApplication.command('get current weather')
def command_current(self, city):
try:
for backend, current in self.do('get_current', city):
self.format(current, backend.name)
self.format(current)
except CallErrors, e:
for error in e:
if isinstance(error, CityNotFound):
@ -57,7 +57,7 @@ class WetBoobs(ConsoleApplication):
def command_forecasts(self, city):
try:
for backend, forecast in self.do('iter_forecast', city):
self.format(forecast, backend.name)
self.format(forecast)
except CallErrors, e:
for error in e:
if isinstance(error, CityNotFound):

View file

@ -21,7 +21,7 @@ if sys.version_info[:2] <= (2, 5):
from weboob.tools.property import property
from .base import IBaseCap
from .base import IBaseCap, CapBaseObject
__all__ = ['Account', 'AccountNotFound', 'ICapBank', 'Operation']
@ -31,9 +31,10 @@ class AccountNotFound(Exception):
pass
class Account(object):
class Account(CapBaseObject):
FIELDS = ('label', 'balance', 'coming')
def __init__(self):
self.id = 0
CapBaseObject.__init__(self, 0)
self.label = ''
self._balance = 0.0
self._coming = 0.0
@ -59,8 +60,10 @@ class Account(object):
return u"<Account id='%s' label='%s'>" % (self.id, self.label)
class Operation(object):
class Operation(CapBaseObject):
FIELDS = ('date', 'label', 'amount')
def __init__(self):
CapBaseObject(self, 0)
self.date = None
self._label = u''
self._amount = 0.0

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010 Christophe Benz
# Copyright(C) 2010 Christophe Benz, Romain Bignon
#
# 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
@ -16,7 +16,9 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
__all__ = ['IBaseCap', 'NotLoaded', 'LoadingError']
from weboob.tools.misc import iter_fields
__all__ = ['IBaseCap', 'NotLoaded', 'LoadingError', 'CapBaseObject']
class NotLoadedMeta(type):
@ -45,3 +47,20 @@ class LoadingError(object):
class IBaseCap(object):
pass
class CapBaseObject(object):
FIELDS = None
def __init__(self, id, backend=None):
self.id = id
self.backend = backend
def iter_fields(self):
if self.FIELDS is None:
for key, value in iter_fields(self):
if key != 'backend':
yield key, value
else:
yield 'id', self.id
for attrstr in self.FIELDS:
yield attrstr, getattr(self, attrstr)

View file

@ -18,7 +18,7 @@
import datetime
from .base import IBaseCap
from .base import IBaseCap, CapBaseObject
__all__ = ['ChatException', 'ICapChat']
@ -28,8 +28,11 @@ class ChatException(Exception):
pass
class ChatMessage(object):
def __init__(self, id_from, id_to, message, date=None):
class ChatMessage(CapBaseObject):
FIELDS = ('id_from', 'id_to', 'date', 'message')
def __init__(self, id_from, id_to, message, date=None):
CapBaseObject.__init__(self, '%s.%s' % (id_from, id_to))
self.id_from = id_from
self.id_to = id_to
self.message = message

View file

@ -16,7 +16,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from .base import IBaseCap
from .base import IBaseCap, CapBaseObject
from weboob.tools.ordereddict import OrderedDict
@ -50,14 +50,16 @@ class ContactPhoto(object):
def __repr__(self):
return u'<ContactPhoto "%s" data=%do tndata=%do>' % (self.name, len(self.data), len(self.thumbnail_data))
class Contact(object):
class Contact(CapBaseObject):
FIELDS = ('name', 'status', 'status_msg', 'summary', 'avatar', 'photos', 'profile')
STATUS_ONLINE = 0x001
STATUS_AWAY = 0x002
STATUS_OFFLINE = 0x004
STATUS_ALL = 0xfff
def __init__(self, id, name, status):
self.id = id
CapBaseObject.__init__(self, id)
self.name = name
self.status = status
self.status_msg = u''
@ -74,17 +76,6 @@ class Contact(object):
for key, value in kwargs.iteritems():
setattr(photo, key, value)
def iter_fields(self):
return {'id': self.id,
'name': self.name,
'status': self.status,
'status_msg': self.status_msg,
'summary': self.summary,
'avatar': self.avatar,
'photos': self.photos,
'profile': self.profile,
}.iteritems()
class ICapContact(IBaseCap):
def iter_contacts(self, status=Contact.STATUS_ALL, ids=None):
"""

View file

@ -16,13 +16,16 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from .cap import ICap
from .base import IBaseCap, CapBaseObject
__all__ = ('IpLocation', 'ICapGeolocIp')
class IpLocation(object):
class IpLocation(CapBaseObject):
FIELDS = ('city', 'region', 'zipcode', 'country', 'lt', 'lg', 'host', 'tls', 'isp')
def __init__(self, ipaddr):
CapBaseObject.__init__(self, ipaddr)
self.ipaddr = ipaddr
self.city = None
self.region = None
@ -34,6 +37,6 @@ class IpLocation(object):
self.tld = None
self.isp = None
class ICapGeolocIp(ICap):
class ICapGeolocIp(IBaseCap):
def get_location(self, ipaddr):
raise NotImplementedError()

View file

@ -16,15 +16,19 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from .base import IBaseCap
from .base import IBaseCap, CapBaseObject, NotLoaded
__all__ = ['ICapTorrent', 'Torrent']
class Torrent(object):
def __init__(self, id, name, date=None, size=0.0, url=u'', seeders=0, leechers=0, files=[], description=u''):
self.id = id
class Torrent(CapBaseObject):
FIELDS = ('name', 'size', 'date', 'url', 'seeders', 'leechers', 'files', 'description')
def __init__(self, id, name, date=NotLoaded, size=NotLoaded, url=NotLoaded,
seeders=NotLoaded, leechers=NotLoaded, files=NotLoaded,
description=NotLoaded):
CapBaseObject.__init__(self, id)
self.name = name
self.date = date
self.size = size

View file

@ -18,7 +18,7 @@
from datetime import time
from .base import IBaseCap
from .base import IBaseCap, CapBaseObject
__all__ = ['Departure', 'ICapTravel', 'Station']
@ -45,18 +45,23 @@ class ICapTravel(IBaseCap):
raise NotImplementedError()
class Station(object):
def __init__(self, _id, name):
self.id = _id
class Station(CapBaseObject):
FIELDS = ('name',)
def __init__(self, id, name):
CapBaseObject.__init__(self, id)
self.name = name
def __repr__(self):
return "<Station id='%s' name='%s'>" % (self.id, self.name)
class Departure(object):
def __init__(self, _id, _type, _time):
self.id = _id
class Departure(CapBaseObject):
FIELDS = ('type', 'time', 'departure_station', 'arrival_station', 'late', 'information', 'plateform')
def __init__(self, id, _type, _time):
CapBaseObject.__init__(self, id)
self.type = _type
self.time = _time
self.departure_station = u''

View file

@ -16,7 +16,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from .base import IBaseCap, LoadingError, NotLoaded
from .base import IBaseCap, LoadingError, NotLoaded, CapBaseObject
__all__ = ['BaseVideo', 'ICapVideo']
@ -37,7 +37,9 @@ class VideoThumbnail(object):
return self.data is not NotLoaded
class BaseVideo(object):
class BaseVideo(CapBaseObject):
FIELDS = ('title', 'url', 'author', 'duration', 'date', 'rating', 'rating_max', 'thumbnail', 'nsfw')
def __init__(self, _id, title=NotLoaded, url=NotLoaded, author=NotLoaded, duration=NotLoaded, date=NotLoaded,
rating=NotLoaded, rating_max=NotLoaded, thumbnail=NotLoaded, thumbnail_url=None, nsfw=False):
self.id = unicode(_id)

View file

@ -16,14 +16,15 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from .base import IBaseCap
from .base import IBaseCap, CapBaseObject
__all__ = ['City', 'CityNotFound', 'Current', 'Forecast', 'ICapWeather']
class Forecast(object):
class Forecast(CapBaseObject):
def __init__(self, date, low, high, text, unit):
CapBaseObject.__init__(self, date)
self.date = date
self.low = low
self.high = high
@ -31,17 +32,18 @@ class Forecast(object):
self.unit = unit
class Current(object):
class Current(CapBaseObject):
def __init__(self, date, temp, text, unit):
CapBaseObject.__init__(self, date)
self.date = date
self.temp = temp
self.text = text
self.unit = unit
class City(object):
def __init__(self, city_id, name):
self.city_id = city_id
class City(CapBaseObject):
def __init__(self, id, name):
self.id = id
self.name = name

View file

@ -22,6 +22,8 @@ from copy import copy
import logging
from logging import debug
from threading import Thread, Event, RLock, Timer
from weboob.capabilities.base import CapBaseObject
from weboob.tools.misc import get_backtrace
@ -80,6 +82,8 @@ class BackendsCall(object):
def _store_result(self, backend, result):
with self.mutex:
if isinstance(result, CapBaseObject):
result.backend = backend.name
self.responses.append((backend, result))
self.response_event.set()

View file

@ -241,10 +241,9 @@ class ConsoleApplication(BaseApplication):
def set_formatter_header(self, string):
self.formatter.set_header(string)
def format(self, result, backend_name=None):
def format(self, result):
try:
self.formatter.format(obj=result, backend_name=backend_name,
selected_fields=self.selected_fields, condition=self.condition)
self.formatter.format(obj=result, selected_fields=self.selected_fields, condition=self.condition)
except FieldNotFound, e:
logging.error(e)
except ResultsConditionException, e:

View file

@ -16,6 +16,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from weboob.capabilities.base import CapBaseObject
from weboob.tools.misc import iter_fields
from weboob.tools.ordereddict import OrderedDict
@ -43,7 +44,7 @@ class IFormatter(object):
def flush(self):
raise NotImplementedError()
def format(self, obj, backend_name=None, selected_fields=None, condition=None):
def format(self, obj, selected_fields=None, condition=None):
"""
Format an object to be human-readable.
An object has fields which can be selected, and the objects
@ -52,15 +53,17 @@ class IFormatter(object):
call it. It can be used to specify the fields order.
@param obj [object] object to format
@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
@return a string of the formatted object
"""
assert isinstance(obj, (dict, CapBaseObject))
if isinstance(obj, dict):
item = obj
else:
item = self.to_dict(obj, backend_name, condition, selected_fields)
item = self.to_dict(obj, condition, selected_fields)
if item is None:
return None
formatted = self.format_dict(item=item)
@ -82,33 +85,25 @@ class IFormatter(object):
def set_header(self, string):
raise NotImplementedError()
def to_dict(self, obj, backend_name=None, condition=None, selected_fields=None):
def to_dict(self, obj, condition=None, selected_fields=None):
def iter_select_and_decorate(d):
if hasattr(obj, '__id__'):
id_attr = getattr(obj, '__id__')
if not isinstance(id_attr, (set, list, tuple)):
id_attr = (id_attr,)
id_fields = id_attr
else:
id_fields = ('id',)
if selected_fields is None or '*' in selected_fields:
for k, v in d:
if k in id_fields and backend_name is not None:
v = self.build_id(v, backend_name)
yield k, v
fields = d.iterkeys()
else:
d = dict(d)
for selected_field in selected_fields:
v = d[selected_field]
if selected_field in id_fields and backend_name is not None:
v = self.build_id(v, backend_name)
try:
yield selected_field, v
except KeyError:
raise FieldNotFound(selected_field)
fields = selected_fields
fields_iterator = obj.iter_fields() if hasattr(obj, 'iter_fields') else iter_fields(obj)
d = dict(fields_iterator)
for key in fields:
try:
value = d[key]
except KeyError:
raise FieldNotFound(key)
if key == 'id' and obj.backend is not None:
value = self.build_id(value, obj.backend)
yield key, value
fields_iterator = obj.iter_fields()
d = OrderedDict(fields_iterator)
if condition is not None and not condition.is_valid(d):
return None
return OrderedDict([(k, v) for k, v in iter_select_and_decorate(d.iteritems())])
return OrderedDict([(k, v) for k, v in iter_select_and_decorate(d)])