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' COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
def main(self, argv): def main(self, argv):
self.load_configured_backends(ICapBank)
return self.process_command(*argv[1:]) return self.process_command(*argv[1:])
@ConsoleApplication.command('List every available accounts') @ConsoleApplication.command('List every available accounts')
def command_list(self): def command_list(self):
self.load_configured_backends(ICapBank)
try: try:
for backend, account in self.do('iter_accounts'): for backend, account in self.do('iter_accounts'):
self.format(account, backend.name) self.format(account)
except weboob.core.CallErrors, errors: except weboob.core.CallErrors, errors:
for backend, error, backtrace in errors: for backend, error, backtrace in errors:
if isinstance(error, weboob.tools.browser.BrowserIncorrectPassword): if isinstance(error, weboob.tools.browser.BrowserIncorrectPassword):
@ -52,19 +52,13 @@ class Boobank(ConsoleApplication):
@ConsoleApplication.command('Display all future operations') @ConsoleApplication.command('Display all future operations')
def command_coming(self, id): 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): def do(backend):
account = backend.get_account(id) account = backend.get_account(id)
return backend.iter_operations(account) return backend.iter_operations(account)
try: for backend, operation in self.do(do):
for backend, operation in self.do(do): self.format(operation)
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))

View file

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

View file

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

View file

@ -37,7 +37,7 @@ class Traveloob(ConsoleApplication):
def command_stations(self, pattern): def command_stations(self, pattern):
self.load_backends(ICapTravel) self.load_backends(ICapTravel)
for backend, station in self.do('iter_station_search', pattern): 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') @ConsoleApplication.command('List all departures for a given station')
def command_departures(self, station, arrival=None): def command_departures(self, station, arrival=None):
@ -59,4 +59,4 @@ class Traveloob(ConsoleApplication):
self.load_backends(ICapTravel, names=backends) self.load_backends(ICapTravel, names=backends)
for backend, departure in self.do('iter_station_departures', station_id, arrival_id): 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): for backend, video in self.do('get_video', _id):
if video is None: if video is None:
continue continue
self.format(video, backend.name) self.format(video)
@ConsoleApplication.command('Search for videos') @ConsoleApplication.command('Search for videos')
def command_search(self, pattern=None): 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') 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, for backend, video in self.do('iter_search_results', pattern=pattern, nsfw=self.options.nsfw,
max_results=self.options.count): 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) self.load_backends(cap_s)
for backend, obj in self.do(cmd, *args): for backend, obj in self.do(cmd, *args):
self.format(obj, backend.name) self.format(obj)
return 0 return 0

View file

@ -42,7 +42,7 @@ class Weboorrents(ConsoleApplication):
found = 0 found = 0
for backend, torrent in self.do('get_torrent', _id, backends=backend_name): for backend, torrent in self.do('get_torrent', _id, backends=backend_name):
if torrent: if torrent:
self.format(torrent, backend.name) self.format(torrent)
found = 1 found = 1
if not found: if not found:
@ -67,4 +67,4 @@ class Weboorrents(ConsoleApplication):
def command_search(self, pattern=None): def command_search(self, pattern=None):
self.set_formatter_header(u'Search pattern: %s' % pattern if pattern else u'Latest torrents') 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): 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') @ConsoleApplication.command('search cities')
def command_search(self, pattern): def command_search(self, pattern):
for backend, city in self.do('iter_city_search', pattern): for backend, city in self.do('iter_city_search', pattern):
self.format(city, backend.name) self.format(city)
@ConsoleApplication.command('get current weather') @ConsoleApplication.command('get current weather')
def command_current(self, city): def command_current(self, city):
try: try:
for backend, current in self.do('get_current', city): for backend, current in self.do('get_current', city):
self.format(current, backend.name) self.format(current)
except CallErrors, e: except CallErrors, e:
for error in e: for error in e:
if isinstance(error, CityNotFound): if isinstance(error, CityNotFound):
@ -57,7 +57,7 @@ class WetBoobs(ConsoleApplication):
def command_forecasts(self, city): def command_forecasts(self, city):
try: try:
for backend, forecast in self.do('iter_forecast', city): for backend, forecast in self.do('iter_forecast', city):
self.format(forecast, backend.name) self.format(forecast)
except CallErrors, e: except CallErrors, e:
for error in e: for error in e:
if isinstance(error, CityNotFound): if isinstance(error, CityNotFound):

View file

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

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- 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 # 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 # 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. # 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): class NotLoadedMeta(type):
@ -45,3 +47,20 @@ class LoadingError(object):
class IBaseCap(object): class IBaseCap(object):
pass 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 import datetime
from .base import IBaseCap from .base import IBaseCap, CapBaseObject
__all__ = ['ChatException', 'ICapChat'] __all__ = ['ChatException', 'ICapChat']
@ -28,8 +28,11 @@ class ChatException(Exception):
pass pass
class ChatMessage(object): class ChatMessage(CapBaseObject):
def __init__(self, id_from, id_to, message, date=None): 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_from = id_from
self.id_to = id_to self.id_to = id_to
self.message = message self.message = message

View file

@ -16,7 +16,7 @@
# 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 .base import IBaseCap from .base import IBaseCap, CapBaseObject
from weboob.tools.ordereddict import OrderedDict from weboob.tools.ordereddict import OrderedDict
@ -50,14 +50,16 @@ class ContactPhoto(object):
def __repr__(self): def __repr__(self):
return u'<ContactPhoto "%s" data=%do tndata=%do>' % (self.name, len(self.data), len(self.thumbnail_data)) 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_ONLINE = 0x001
STATUS_AWAY = 0x002 STATUS_AWAY = 0x002
STATUS_OFFLINE = 0x004 STATUS_OFFLINE = 0x004
STATUS_ALL = 0xfff STATUS_ALL = 0xfff
def __init__(self, id, name, status): def __init__(self, id, name, status):
self.id = id CapBaseObject.__init__(self, id)
self.name = name self.name = name
self.status = status self.status = status
self.status_msg = u'' self.status_msg = u''
@ -74,17 +76,6 @@ class Contact(object):
for key, value in kwargs.iteritems(): for key, value in kwargs.iteritems():
setattr(photo, key, value) 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): class ICapContact(IBaseCap):
def iter_contacts(self, status=Contact.STATUS_ALL, ids=None): 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. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from .cap import ICap from .base import IBaseCap, CapBaseObject
__all__ = ('IpLocation', 'ICapGeolocIp') __all__ = ('IpLocation', 'ICapGeolocIp')
class IpLocation(object): class IpLocation(CapBaseObject):
FIELDS = ('city', 'region', 'zipcode', 'country', 'lt', 'lg', 'host', 'tls', 'isp')
def __init__(self, ipaddr): def __init__(self, ipaddr):
CapBaseObject.__init__(self, ipaddr)
self.ipaddr = ipaddr self.ipaddr = ipaddr
self.city = None self.city = None
self.region = None self.region = None
@ -34,6 +37,6 @@ class IpLocation(object):
self.tld = None self.tld = None
self.isp = None self.isp = None
class ICapGeolocIp(ICap): class ICapGeolocIp(IBaseCap):
def get_location(self, ipaddr): def get_location(self, ipaddr):
raise NotImplementedError() raise NotImplementedError()

View file

@ -16,15 +16,19 @@
# 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 .base import IBaseCap from .base import IBaseCap, CapBaseObject, NotLoaded
__all__ = ['ICapTorrent', 'Torrent'] __all__ = ['ICapTorrent', 'Torrent']
class Torrent(object): class Torrent(CapBaseObject):
def __init__(self, id, name, date=None, size=0.0, url=u'', seeders=0, leechers=0, files=[], description=u''): FIELDS = ('name', 'size', 'date', 'url', 'seeders', 'leechers', 'files', 'description')
self.id = id
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.name = name
self.date = date self.date = date
self.size = size self.size = size

View file

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

View file

@ -16,7 +16,7 @@
# 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 .base import IBaseCap, LoadingError, NotLoaded from .base import IBaseCap, LoadingError, NotLoaded, CapBaseObject
__all__ = ['BaseVideo', 'ICapVideo'] __all__ = ['BaseVideo', 'ICapVideo']
@ -37,7 +37,9 @@ class VideoThumbnail(object):
return self.data is not NotLoaded 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, 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): rating=NotLoaded, rating_max=NotLoaded, thumbnail=NotLoaded, thumbnail_url=None, nsfw=False):
self.id = unicode(_id) self.id = unicode(_id)

View file

@ -16,14 +16,15 @@
# 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 .base import IBaseCap from .base import IBaseCap, CapBaseObject
__all__ = ['City', 'CityNotFound', 'Current', 'Forecast', 'ICapWeather'] __all__ = ['City', 'CityNotFound', 'Current', 'Forecast', 'ICapWeather']
class Forecast(object): class Forecast(CapBaseObject):
def __init__(self, date, low, high, text, unit): def __init__(self, date, low, high, text, unit):
CapBaseObject.__init__(self, date)
self.date = date self.date = date
self.low = low self.low = low
self.high = high self.high = high
@ -31,17 +32,18 @@ class Forecast(object):
self.unit = unit self.unit = unit
class Current(object): class Current(CapBaseObject):
def __init__(self, date, temp, text, unit): def __init__(self, date, temp, text, unit):
CapBaseObject.__init__(self, date)
self.date = date self.date = date
self.temp = temp self.temp = temp
self.text = text self.text = text
self.unit = unit self.unit = unit
class City(object): class City(CapBaseObject):
def __init__(self, city_id, name): def __init__(self, id, name):
self.city_id = city_id self.id = id
self.name = name self.name = name

View file

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

View file

@ -241,10 +241,9 @@ class ConsoleApplication(BaseApplication):
def set_formatter_header(self, string): 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):
try: try:
self.formatter.format(obj=result, backend_name=backend_name, self.formatter.format(obj=result, selected_fields=self.selected_fields, condition=self.condition)
selected_fields=self.selected_fields, condition=self.condition)
except FieldNotFound, e: except FieldNotFound, e:
logging.error(e) logging.error(e)
except ResultsConditionException, e: except ResultsConditionException, e:

View file

@ -16,6 +16,7 @@
# 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 weboob.capabilities.base import CapBaseObject
from weboob.tools.misc import iter_fields from weboob.tools.misc import iter_fields
from weboob.tools.ordereddict import OrderedDict from weboob.tools.ordereddict import OrderedDict
@ -43,7 +44,7 @@ class IFormatter(object):
def flush(self): def flush(self):
raise NotImplementedError() 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. Format an object to be human-readable.
An object has fields which can be selected, and the objects 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. call it. It can be used to specify the fields order.
@param obj [object] object to format @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 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
""" """
assert isinstance(obj, (dict, CapBaseObject))
if isinstance(obj, dict): if isinstance(obj, dict):
item = obj item = obj
else: else:
item = self.to_dict(obj, backend_name, condition, selected_fields) item = self.to_dict(obj, condition, selected_fields)
if item is None: if item is None:
return None return None
formatted = self.format_dict(item=item) formatted = self.format_dict(item=item)
@ -82,33 +85,25 @@ class IFormatter(object):
def set_header(self, string): def set_header(self, string):
raise NotImplementedError() 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): 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: if selected_fields is None or '*' in selected_fields:
for k, v in d: fields = d.iterkeys()
if k in id_fields and backend_name is not None:
v = self.build_id(v, backend_name)
yield k, v
else: else:
d = dict(d) fields = selected_fields
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_iterator = obj.iter_fields() if hasattr(obj, 'iter_fields') else iter_fields(obj) for key in fields:
d = dict(fields_iterator) 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): if condition is not None and not condition.is_valid(d):
return None 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)])