diff --git a/weboob/applications/boobank/boobank.py b/weboob/applications/boobank/boobank.py
index fe88476f..9c5620f1 100644
--- a/weboob/applications/boobank/boobank.py
+++ b/weboob/applications/boobank/boobank.py
@@ -23,7 +23,7 @@ import sys
from weboob.capabilities.bank import ICapBank, Account, Transaction
from weboob.tools.application.repl import ReplApplication
-from weboob.tools.application.formatters.iformatter import IFormatter
+from weboob.tools.application.formatters.iformatter import IFormatter, PrettyFormatter
__all__ = ['Boobank']
@@ -32,22 +32,16 @@ __all__ = ['Boobank']
class QifFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'date', 'raw', 'amount', 'category')
- count = 0
+ def start_format(self, **kwargs):
+ self.output(u'!type:Bank')
- def flush(self):
- self.count = 0
-
- def format_dict(self, item):
- result = u''
- if self.count == 0:
- result += u'!type:Bank\n'
- result += u'D%s\n' % item['date'].strftime('%d/%m/%y')
- result += u'T%s\n' % item['amount']
- if item['category']:
- result += u'N%s\n' % item['category']
- result += u'M%s\n' % item['raw']
+ def format_obj(self, obj, alias):
+ result = u'D%s\n' % obj.date.strftime('%d/%m/%y')
+ result += u'T%s\n' % obj.amount
+ if obj.category:
+ result += u'N%s\n' % obj.category
+ result += u'M%s\n' % obj.raw
result += u'^\n'
- self.count += 1
return result
@@ -55,110 +49,78 @@ class TransactionsFormatter(IFormatter):
MANDATORY_FIELDS = ('date', 'label', 'amount')
TYPES = ['', 'Transfer', 'Order', 'Check', 'Deposit', 'Payback', 'Withdrawal', 'Card', 'Loan', 'Bank']
- count = 0
+ def start_format(self, **kwargs):
+ self.output(' Date Category Label Amount ')
+ self.output('------------+------------+---------------------------------------------------+-----------')
- def flush(self):
- if self.count < 1:
- return
- self.count = 0
-
- def format_dict(self, item):
- self.count += 1
-
- result = u''
- if self.count == 1:
- result += ' Date Category Label Amount \n'
- result += '------------+------------+---------------------------------------------------+-----------\n'
-
- if item['category']:
- _type = item['category']
+ def format_obj(self, obj, alias):
+ if hasattr(obj, 'category') and obj.category:
+ _type = obj.category
else:
try:
- _type = self.TYPES[item['type']]
- except IndexError:
+ _type = self.TYPES[obj.type]
+ except (IndexError,AttributeError):
_type = ''
- label = item['label']
- if not label:
- label = item['raw']
- result += ' %-10s %-12s %-50s %10.2f' % (item['date'].strftime('%Y-%m-%d'), _type, label[:50], item['amount'])
- return result
+ label = obj.label
+ if not label and hasattr(obj, 'raw'):
+ label = obj.raw
+ return ' %-10s %-12s %-50s %10.2f' % (obj.date.strftime('%Y-%m-%d'), _type[:12], label[:50], obj.amount)
class TransferFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'date', 'origin', 'recipient', 'amount')
- def flush(self):
- pass
-
- def format_dict(self, item):
- result = u'------- Transfer %s -------\n' % item['id']
- result += u'Date: %s\n' % item['date']
- result += u'Origin: %s\n' % item['origin']
- result += u'Recipient: %s\n' % item['recipient']
- result += u'Amount: %.2f\n' % item['amount']
+ def format_obj(self, obj, alias):
+ result = u'------- Transfer %s -------\n' % obj.fillud
+ result += u'Date: %s\n' % obj.date
+ result += u'Origin: %s\n' % obj.origin
+ result += u'Recipient: %s\n' % obj.recipient
+ result += u'Amount: %.2f\n' % obj.amount
return result
-class RecipientListFormatter(IFormatter):
+class RecipientListFormatter(PrettyFormatter):
MANDATORY_FIELDS = ('id', 'label')
- count = 0
+ def start_format(self, **kwargs):
+ self.output('Available recipients:')
- def flush(self):
- self.count = 0
-
- def format_dict(self, item):
- self.count += 1
-
- if self.interactive:
- backend = item['id'].split('@', 1)[1]
- id = '#%d (%s)' % (self.count, backend)
- else:
- id = item['id']
-
- return u'%s %-30s %s %s' % (self.BOLD, id, self.NC, item['label'])
+ def get_title(self, obj):
+ return obj.label
class AccountListFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'label', 'balance', 'coming')
- count = 0
tot_balance = Decimal(0)
tot_coming = Decimal(0)
- def flush(self):
- if self.count < 1:
- return
+ def start_format(self, **kwargs):
+ self.output(' %s Account Balance Coming ' % ((' ' * 15) if not self.interactive else ''))
+ self.output('------------------------------------------%s+----------+----------' % (('-' * 15) if not self.interactive else ''))
- result = u'------------------------------------------%s+----------+----------\n' % (('-' * 15) if not self.interactive else '')
- result += u'%s Total %8s %8s' % ((' ' * 15) if not self.interactive else '',
- '%.2f' % self.tot_balance, '%.2f' % self.tot_coming)
- self.after_format(result)
+ def format_obj(self, obj, alias):
+ if alias is not None:
+ id = '#%s (%s)' % (alias, obj.backend)
+ else:
+ id = obj.fullid
+
+ result = (u' %s%-' + (u'15' if alias is not None else '30') + u's%s %-25s %8s %8s') % \
+ (self.BOLD, id, self.NC,
+ obj.label, '%.2f' % obj.balance, '%.2f' % (obj.coming or Decimal(0.0)))
+
+ self.tot_balance += obj.balance
+ if obj.coming:
+ self.tot_coming += obj.coming
+ return result
+
+ def flush(self):
+ self.output(u'------------------------------------------%s+----------+----------' % (('-' * 15) if not self.interactive else ''))
+ self.output(u'%s Total %8s %8s' % ((' ' * 15) if not self.interactive else '',
+ '%.2f' % self.tot_balance, '%.2f' % self.tot_coming))
self.tot_balance = Decimal(0)
self.tot_coming = Decimal(0)
- self.count = 0
-
- def format_dict(self, item):
- self.count += 1
- if self.interactive:
- backend = item['id'].split('@', 1)[1]
- id = '#%d (%s)' % (self.count, backend)
- else:
- id = item['id']
-
- result = u''
- if self.count == 1:
- result += ' %s Account Balance Coming \n' % ((' ' * 15) if not self.interactive else '')
- result += '------------------------------------------%s+----------+----------\n' % (('-' * 15) if not self.interactive else '')
- result += (u' %s%-' + (u'15' if self.interactive else '30') + u's%s %-25s %8s %8s') % \
- (self.BOLD, id, self.NC,
- item['label'], '%.2f' % item['balance'], '%.2f' % (item['coming'] or Decimal(0.0)))
-
- self.tot_balance += item['balance']
- if item['coming']:
- self.tot_coming += item['coming']
- return result
class Boobank(ReplApplication):
@@ -219,6 +181,7 @@ class Boobank(ReplApplication):
account = backend.get_account(id)
return backend.iter_history(account)
+ self.start_format()
for backend, operation in self.do(do, backends=names):
self.format(operation)
self.flush()
@@ -241,6 +204,7 @@ class Boobank(ReplApplication):
account = backend.get_account(id)
return backend.iter_coming(account)
+ self.start_format(id=id)
for backend, operation in self.do(do, backends=names):
self.format(operation)
self.flush()
@@ -273,9 +237,10 @@ class Boobank(ReplApplication):
self.set_formatter('recipient_list')
self.set_formatter_header(u'Available recipients')
names = (backend_name_from,) if backend_name_from is not None else None
+
+ self.start_format()
for backend, recipient in self.do('iter_transfer_recipients', id_from, backends=names):
- self.format(recipient)
- self.add_object(recipient)
+ self.cached_format(recipient)
self.flush()
return 0
@@ -295,6 +260,7 @@ class Boobank(ReplApplication):
names = (backend_name,) if backend_name is not None else None
+ self.start_format()
for backend, transfer in self.do('transfer', id_from, id_to, amount, reason, backends=names):
self.format(transfer)
self.flush()
diff --git a/weboob/applications/boobill/boobill.py b/weboob/applications/boobill/boobill.py
index 61018185..ba16b949 100644
--- a/weboob/applications/boobill/boobill.py
+++ b/weboob/applications/boobill/boobill.py
@@ -22,30 +22,17 @@ import sys
from weboob.capabilities.bill import ICapBill, Detail, Subscription
from weboob.tools.application.repl import ReplApplication
-from weboob.tools.application.formatters.iformatter import IFormatter
+from weboob.tools.application.formatters.iformatter import PrettyFormatter
__all__ = ['Boobill']
-class SubscriptionsFormatter(IFormatter):
+class SubscriptionsFormatter(PrettyFormatter):
MANDATORY_FIELDS = ('id', 'label')
- count = 0
-
- def flush(self):
- self.count = 0
-
- def format_dict(self, item):
- self.count += 1
-
- if self.interactive:
- backend = item['id'].split('@', 1)[1]
- id = '#%d (%s)' % (self.count, backend)
- else:
- id = item['id']
-
- return u'%s%s%s %s' % (self.BOLD, id, self.NC, item['label'])
+ def get_title(self, obj):
+ return obj.label
class Boobill(ReplApplication):
APPNAME = 'boobill'
@@ -72,9 +59,9 @@ class Boobill(ReplApplication):
List subscriptions
"""
+ self.start_format()
for backend, subscription in self.do('iter_subscription'):
- self.add_object(subscription)
- self.format(subscription)
+ self.cached_format(subscription)
self.flush()
def do_details(self, id):
@@ -90,15 +77,15 @@ class Boobill(ReplApplication):
return 2
names = (backend_name,) if backend_name is not None else None
- def do(backend):
- return backend.get_details(id)
-
- # XXX: should be generated by backend?
+ # XXX: should be generated by backend? -Flo
+ # XXX: no, but you should do it in a specific formatter -romain
mysum = Detail()
mysum.label = "Sum"
mysum.infos = "Generated by boobill"
mysum.price = 0.
- for backend, detail in self.do(do, backends=names):
+
+ self.start_format()
+ for backend, detail in self.do('get_details', id, backends=names):
self.format(detail)
mysum.price = detail.price + mysum.price
@@ -118,10 +105,8 @@ class Boobill(ReplApplication):
return 2
names = (backend_name,) if backend_name is not None else None
- def do(backend):
- return backend.iter_history(id)
-
- for backend, history in self.do(do, backends=names):
+ self.start_format()
+ for backend, history in self.do('iter_history', id, backends=names):
self.format(history)
self.flush()
@@ -129,7 +114,7 @@ class Boobill(ReplApplication):
"""
bills Id
- Get the list of bills documents for subscription
+ Get the list of bills documents for subscription
id is the identifier of the backend
"""
@@ -139,10 +124,8 @@ class Boobill(ReplApplication):
return 2
names = (backend_name,) if backend_name is not None else None
- def do(backend):
- return backend.iter_bills(id)
-
- for backend, date in self.do(do, backends=names):
+ self.start_format()
+ for backend, date in self.do('iter_bills', id, backends=names):
self.format(date)
self.flush()
@@ -150,7 +133,7 @@ class Boobill(ReplApplication):
"""
download Id [FILENAME]
- download the bill
+ download the bill
id is the identifier of the bill (hint: try bills command)
FILENAME is where to write the file. If FILENAME is '-',
the file is written to stdout.
@@ -178,5 +161,3 @@ class Boobill(ReplApplication):
print >>sys.stderr, 'Unable to write bill in "%s": %s' % (dest, e)
return 1
return
-
-
diff --git a/weboob/applications/boobmsg/boobmsg.py b/weboob/applications/boobmsg/boobmsg.py
index 25b931e7..650cc670 100644
--- a/weboob/applications/boobmsg/boobmsg.py
+++ b/weboob/applications/boobmsg/boobmsg.py
@@ -35,50 +35,48 @@ __all__ = ['Boobmsg']
class XHtmlFormatter(IFormatter):
- def flush(self):
- pass
+ MANDATORY_FIELDS = ('title', 'date', 'sender', 'signature', 'content')
- def format_dict(self, item):
+ def format_obj(self, obj, alias):
result = "
\n"
- result += "
%s
" % (item['title'])
+ result += "
%s
" % (obj.title)
result += "
"
- result += "- Date
- %s
" % (item['date'])
- result += "- Sender
- %s
" % (item['sender'])
- result += "- Signature
- %s
" % (item['signature'])
+ result += "- Date
- %s
" % (obj.date)
+ result += "- Sender
- %s
" % (obj.sender)
+ result += "- Signature
- %s
" % (obj.signature)
result += "
"
- result += "
%s
" % (item['content'])
+ result += "
%s
" % (obj.content)
result += "
\n"
return result
class MessageFormatter(IFormatter):
- def flush(self):
- pass
+ MANDATORY_FIELDS = ('title', 'date', 'sender', 'signature', 'content')
- def format_dict(self, item):
+ def format_obj(self, obj, alias):
result = u'%sTitle:%s %s\n' % (self.BOLD,
- self.NC, item['title'])
+ self.NC, obj.title)
result += u'%sDate:%s %s\n' % (self.BOLD,
- self.NC, item['date'])
+ self.NC, obj.date)
result += u'%sFrom:%s %s\n' % (self.BOLD,
- self.NC, item['sender'])
- if item['receivers']:
+ self.NC, obj.sender)
+ if hasattr(obj, 'receivers') and obj.receivers:
result += u'%sTo:%s %s\n' % (self.BOLD,
self.NC,
- ', '.join(item['receivers']))
+ ', '.join(obj.receivers))
- if item['flags'] & Message.IS_HTML:
- content = html2text(item['content'])
+ if obj.flags & Message.IS_HTML:
+ content = html2text(obj.content)
else:
- content = item['content']
+ content = obj.content
result += '\n%s' % content
- if item['signature']:
- if item['flags'] & Message.IS_HTML:
- signature = html2text(item['signature'])
+ if obj.signature:
+ if obj.flags & Message.IS_HTML:
+ signature = html2text(obj.signature)
else:
- signature = item['signature']
+ signature = obj.signature
result += '\n-- \n%s' % signature
return result
@@ -92,36 +90,34 @@ class MessagesListFormatter(IFormatter):
def flush(self):
self.count = 0
- def format_dict(self, item):
+ def format_obj(self, obj, alias):
if not self._list_messages:
- return self.format_dict_thread(item)
+ return self.format_dict_thread(obj, alias)
else:
- return self.format_dict_messages(item)
+ return self.format_dict_messages(obj, alias)
- def format_dict_thread(self, item):
+ def format_dict_thread(self, obj, alias):
self.count += 1
if self.interactive:
- backend = item['id'].split('@', 1)[1]
result = u'%s* (%d) %s (%s)%s' % (self.BOLD,
- self.count,
- item['title'], backend,
- self.NC)
+ self.count,
+ obj.title, obj.backend,
+ self.NC)
else:
- result = u'%s* (%s) %s%s' % (self.BOLD, item['id'],
- item['title'],
- self.NC)
- if item['date']:
- result += u'\n %s' % item['date']
+ result = u'%s* (%s) %s%s' % (self.BOLD, obj.id,
+ obj.title,
+ self.NC)
+ if obj.date:
+ result += u'\n %s' % obj.date
return result
- def format_dict_messages(self, item):
- backend = item['id'].split('@', 1)[1]
- if item['flags'] == Thread.IS_THREADS:
+ def format_dict_messages(self, obj, alias):
+ if obj.flags == Thread.IS_THREADS:
depth = 0
else:
depth = -1
- result = self.format_message(backend, item['root'], depth)
+ result = self.format_message(obj.backend, obj.root, depth)
return result
def format_message(self, backend, message, depth=0):
@@ -187,25 +183,25 @@ class ProfileFormatter(IFormatter):
result += u'\t' * level + u'%-20s %s\n' % (node.label + ':', value)
return result
- def format_dict(self, item):
- result = u'Nickname: %s\n' % item['name']
- if item['status'] & Contact.STATUS_ONLINE:
+ def format_obj(self, obj):
+ result = u'Nickname: %s\n' % obj.name
+ if obj.status & Contact.STATUS_ONLINE:
s = 'online'
- elif item['status'] & Contact.STATUS_OFFLINE:
+ elif obj.status & Contact.STATUS_OFFLINE:
s = 'offline'
- elif item['status'] & Contact.STATUS_AWAY:
+ elif obj.status & Contact.STATUS_AWAY:
s = 'away'
else:
s = 'unknown'
- result += u'Status: %s (%s)\n' % (s, item['status_msg'])
+ result += u'Status: %s (%s)\n' % (s, obj.status_msg)
result += u'Photos:\n'
- for name, photo in item['photos'].iteritems():
+ for name, photo in obj.photos.iteritems():
result += u'\t%s%s\n' % (photo, ' (hidden)' if photo.hidden else '')
result += u'Profile:\n'
- for head in item['profile'].itervalues():
+ for head in obj.profile.itervalues():
result += self.print_node(head)
result += u'Description:\n'
- for s in item['summary'].split('\n'):
+ for s in obj.summary.split('\n'):
result += u'\t%s\n' % s
return result
@@ -439,7 +435,7 @@ class Boobmsg(ReplApplication):
def do_photos(self, id):
"""
- profile ID
+ photos ID
Display photos of a profile
"""
diff --git a/weboob/applications/boobooks/boobooks.py b/weboob/applications/boobooks/boobooks.py
index 07df1cf2..f6dd2a73 100644
--- a/weboob/applications/boobooks/boobooks.py
+++ b/weboob/applications/boobooks/boobooks.py
@@ -19,33 +19,20 @@
from weboob.capabilities.library import ICapBook, Book
from weboob.tools.application.repl import ReplApplication
-from weboob.tools.application.formatters.iformatter import IFormatter
+from weboob.tools.application.formatters.iformatter import PrettyFormatter
import sys
__all__ = ['Boobooks']
-class RentedListFormatter(IFormatter):
+class RentedListFormatter(PrettyFormatter):
MANDATORY_FIELDS = ('id', 'date', 'author', 'name', 'late')
RED = '[1;31m'
- count = 0
-
- def flush(self):
- self.count = 0
-
- def format_dict(self, item):
- self.count += 1
-
- if self.interactive:
- backend = item['id'].split('@', 1)[1]
- id = '#%d (%s)' % (self.count, backend)
- else:
- id = item['id']
-
- s = u'%s%s%s %s — %s (%s)' % (self.BOLD, id, self.NC, item['author'], item['name'], item['date'])
- if item['late']:
+ def get_title(self, obj):
+ s = u'%s — %s (%s)' % (obj.author, obj.name, obj.date)
+ if obj.late:
s += u' %sLATE!%s' % (self.RED, self.NC)
return s
diff --git a/weboob/applications/boobtracker/boobtracker.py b/weboob/applications/boobtracker/boobtracker.py
index 9b530d15..e291b7bd 100644
--- a/weboob/applications/boobtracker/boobtracker.py
+++ b/weboob/applications/boobtracker/boobtracker.py
@@ -23,7 +23,7 @@ import sys
from weboob.capabilities.bugtracker import ICapBugTracker, Query, Update, Project, Issue
from weboob.tools.application.repl import ReplApplication
-from weboob.tools.application.formatters.iformatter import IFormatter
+from weboob.tools.application.formatters.iformatter import IFormatter, PrettyFormatter
from weboob.tools.misc import html2text
@@ -33,28 +33,25 @@ __all__ = ['BoobTracker']
class IssueFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'project', 'title', 'body', 'author')
- def flush(self):
- pass
-
- def format_dict(self, item):
- result = u'%s%s - #%s - %s%s\n' % (self.BOLD, item['project'].name, item['id'], item['title'], self.NC)
- result += '\n%s\n\n' % item['body']
- result += 'Author: %s (%s)\n' % (item['author'].name, item['creation'])
- if item['status']:
- result += 'Status: %s\n' % item['status'].name
- if item['version']:
- result += 'Version: %s\n' % item['version'].name
- if item['category']:
- result += 'Category: %s\n' % item['category']
- if item['assignee']:
- result += 'Assignee: %s\n' % (item['assignee'].name)
- if item['attachments']:
+ def format_obj(self, obj, alias):
+ result = u'%s%s - #%s - %s%s\n' % (self.BOLD, obj.project.name, obj.fullid, obj.title, self.NC)
+ result += '\n%s\n\n' % obj.body
+ result += 'Author: %s (%s)\n' % (obj.author.name, obj.creation)
+ if hasattr(obj, 'status') and obj.status:
+ result += 'Status: %s\n' % obj.status.name
+ if hasattr(obj, 'version') and obj.version:
+ result += 'Version: %s\n' % obj.version.name
+ if hasattr(obj, 'category') and obj.category:
+ result += 'Category: %s\n' % obj.category
+ if hasattr(obj, 'assignee') and obj.assignee:
+ result += 'Assignee: %s\n' % (obj.assignee.name)
+ if hasattr(obj, 'attachments') and obj.attachments:
result += '\nAttachments:\n'
- for a in item['attachments']:
+ for a in obj.attachments:
result += '* %s%s%s <%s>\n' % (self.BOLD, a.filename, self.NC, a.url)
- if item['history']:
+ if hasattr(obj, 'history') and obj.history:
result += '\nHistory:\n'
- for u in item['history']:
+ for u in obj.history:
result += '* %s%s - %s%s\n' % (self.BOLD, u.date, u.author.name, self.NC)
for change in u.changes:
result += ' - %s%s%s: %s -> %s\n' % (self.BOLD, change.field, self.NC, change.last, change.new)
@@ -62,20 +59,15 @@ class IssueFormatter(IFormatter):
result += html2text(u.message)
return result
-class IssuesListFormatter(IFormatter):
+class IssuesListFormatter(PrettyFormatter):
MANDATORY_FIELDS = ('id', 'project', 'status', 'title', 'category')
- count = 0
+ def get_title(self, obj):
+ return '%s - [%s] %s' % (obj.project.name, obj.status.name, obj.title)
- def flush(self):
- self.count = 0
- pass
+ def get_description(self, obj):
+ return obj.category
- def format_dict(self, item):
- self.count += 1
- result = u'%s* (%s) %s - [%s] %s%s\n' % (self.BOLD, item['id'], item['project'].name, item['status'].name, item['title'], self.NC)
- result += ' %s' % (item['category'])
- return result
class BoobTracker(ReplApplication):
APPNAME = 'boobtracker'
diff --git a/weboob/applications/comparoob/comparoob.py b/weboob/applications/comparoob/comparoob.py
index 1bd5205a..b41ffa35 100644
--- a/weboob/applications/comparoob/comparoob.py
+++ b/weboob/applications/comparoob/comparoob.py
@@ -25,7 +25,7 @@ import sys
from weboob.capabilities.pricecomparison import ICapPriceComparison
from weboob.tools.misc import html2text
from weboob.tools.application.repl import ReplApplication
-from weboob.tools.application.formatters.iformatter import IFormatter
+from weboob.tools.application.formatters.iformatter import IFormatter, PrettyFormatter
__all__ = ['Comparoob']
@@ -34,55 +34,46 @@ __all__ = ['Comparoob']
class PriceFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'cost', 'currency', 'shop', 'product')
- def flush(self):
- pass
-
- def format_dict(self, item):
- if item['message']:
- message = item['message']
+ def format_obj(self, obj, alias):
+ if hasattr(obj, 'message') and obj.message:
+ message = obj.message
else:
- message = '%s (%s)' % (item['shop'].name, item['shop'].location)
+ message = u'%s (%s)' % (obj.shop.name, obj.shop.location)
result = u'%s%s%s\n' % (self.BOLD, message, self.NC)
- result += 'ID: %s\n' % item['id']
- result += 'Product: %s\n' % item['product'].name
- result += 'Cost: %s%s\n' % (item['cost'], item['currency'])
- if item['date']:
- result += 'Date: %s\n' % item['date'].strftime('%Y-%m-%d')
+ result += u'ID: %s\n' % obj.fullid
+ result += u'Product: %s\n' % obj.product.name
+ result += u'Cost: %s%s\n' % (obj.cost, obj.currency)
+ if hasattr(obj, 'date') and obj.date:
+ result += u'Date: %s\n' % obj.date.strftime('%Y-%m-%d')
- result += '\n%sShop:%s\n' % (self.BOLD, self.NC)
- result += '\tName: %s\n' % item['shop'].name
- if item['shop'].location:
- result += '\tLocation: %s\n' % item['shop'].location
- if item['shop'].info:
- result += '\n\t' + html2text(item['shop'].info).replace('\n', '\n\t').strip()
+ result += u'\n%sShop:%s\n' % (self.BOLD, self.NC)
+ result += u'\tName: %s\n' % obj.shop.name
+ if obj.shop.location:
+ result += u'\tLocation: %s\n' % obj.shop.location
+ if obj.shop.info:
+ result += u'\n\t' + html2text(obj.shop.info).replace('\n', '\n\t').strip()
return result
-class PricesFormatter(IFormatter):
- MANDATORY_FIELDS = ('id', 'cost', 'currency', 'shop')
+class PricesFormatter(PrettyFormatter):
+ MANDATORY_FIELDS = ('id', 'cost', 'currency')
- count = 0
-
- def flush(self):
- self.count = 0
-
- def format_dict(self, item):
- self.count += 1
- if item['message']:
- message = item['message']
+ def get_title(self, obj):
+ if hasattr(obj, 'message') and obj.message:
+ message = obj.message
+ elif hasattr(obj, 'shop') and obj.shop:
+ message = '%s (%s)' % (obj.shop.name, obj.shop.location)
else:
- message = '%s (%s)' % (item['shop'].name, item['shop'].location)
+ return u'%s%s' % (obj.cost, obj.currency)
+
+ return u'%s%s - %s' % (obj.cost, obj.currency, message)
+
+ def get_description(self, obj):
+ if obj.date:
+ return obj.date.strftime('%Y-%m-%d')
- if self.interactive:
- backend = item['id'].split('@', 1)[1]
- result = u'%s* (%d) %s%s - %s (%s)%s' % (self.BOLD, self.count, item['cost'], item['currency'], message, backend, self.NC)
- else:
- result = u'%s* (%s) %s%s - %s%s' % (self.BOLD, item['id'], item['cost'], item['currency'], message, self.NC)
- if item['date']:
- result += ' %s' % item['date'].strftime('%Y-%m-%d')
- return result
class Comparoob(ReplApplication):
APPNAME = 'comparoob'
@@ -113,9 +104,10 @@ class Comparoob(ReplApplication):
return 1
self.change_path([u'prices'])
+ self.start_format()
for backend, price in self.do('iter_prices', product):
- self.add_object(price)
- self.format(price)
+ self.cached_format(price)
+ self.flush()
def complete_info(self, text, line, *ignored):
args = line.split(' ')
@@ -131,5 +123,7 @@ class Comparoob(ReplApplication):
if not price:
print >>sys.stderr, 'Price not found: %s' % _id
return 3
+
+ self.start_format()
self.format(price)
self.flush()
diff --git a/weboob/applications/flatboob/flatboob.py b/weboob/applications/flatboob/flatboob.py
index 15779e5e..726f25e5 100644
--- a/weboob/applications/flatboob/flatboob.py
+++ b/weboob/applications/flatboob/flatboob.py
@@ -22,7 +22,7 @@ import sys
from weboob.capabilities.housing import ICapHousing, Query
from weboob.tools.application.repl import ReplApplication
-from weboob.tools.application.formatters.iformatter import IFormatter
+from weboob.tools.application.formatters.iformatter import IFormatter, PrettyFormatter
__all__ = ['Flatboob']
@@ -31,58 +31,47 @@ __all__ = ['Flatboob']
class HousingFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'title', 'cost', 'currency', 'area', 'date', 'text')
- def flush(self):
- pass
+ def format_obj(self, obj, alias):
+ result = u'%s%s%s\n' % (self.BOLD, obj.title, self.NC)
+ result += 'ID: %s\n' % obj.fullid
+ result += 'Cost: %s%s\n' % (obj.cost, obj.currency)
+ result += u'Area: %sm²\n' % (obj.area)
+ if obj.date:
+ result += 'Date: %s\n' % obj.date.strftime('%Y-%m-%d')
+ result += 'Phone: %s\n' % obj.phone
+ if hasattr(obj, 'location') and obj.location:
+ result += 'Location: %s\n' % obj.location
+ if hasattr(obj, 'station') and obj.station:
+ result += 'Station: %s\n' % obj.station
- def format_dict(self, item):
- result = u'%s%s%s\n' % (self.BOLD, item['title'], self.NC)
- result += 'ID: %s\n' % item['id']
- result += 'Cost: %s%s\n' % (item['cost'], item['currency'])
- result += u'Area: %sm²\n' % (item['area'])
- if item['date']:
- result += 'Date: %s\n' % item['date'].strftime('%Y-%m-%d')
- result += 'Phone: %s\n' % item['phone']
- if item['location']:
- result += 'Location: %s\n' % item['location']
- if item['station']:
- result += 'Station: %s\n' % item['station']
-
- if item['photos']:
+ if hasattr(obj, 'photos') and obj.photos:
result += '\n%sPhotos%s\n' % (self.BOLD, self.NC)
- for photo in item['photos']:
+ for photo in obj.photos:
result += ' * %s\n' % photo.url
result += '\n%sDescription%s\n' % (self.BOLD, self.NC)
- result += item['text']
+ result += obj.text
- if item['details']:
+ if hasattr(obj, 'details') and obj.details:
result += '\n\n%sDetails%s\n' % (self.BOLD, self.NC)
- for key, value in item['details'].iteritems():
+ for key, value in obj.details.iteritems():
result += ' %s: %s\n' % (key, value)
return result
-class HousingListFormatter(IFormatter):
+class HousingListFormatter(PrettyFormatter):
MANDATORY_FIELDS = ('id', 'title', 'cost', 'text')
- count = 0
+ def get_title(self, obj):
+ return '%s%s - %s' % (obj.cost, obj.currency, obj.title)
- def flush(self):
- self.count = 0
- pass
-
- def format_dict(self, item):
- self.count += 1
- if self.interactive:
- backend = item['id'].split('@', 1)[1]
- result = u'%s* (%d) %s%s - %s (%s)%s\n' % (self.BOLD, self.count, item['cost'], item['currency'], item['title'], backend, self.NC)
- else:
- result = u'%s* (%s) %s%s - %s%s\n' % (self.BOLD, item['id'], item['cost'], item['currency'], item['title'], self.NC)
- result += ' '
- if item['date']:
- result += '%s - ' % item['date'].strftime('%Y-%m-%d')
- result += item['text']
+ def get_description(self, obj):
+ result = u''
+ if hasattr(obj, 'date') and obj.date:
+ result += '%s - ' % obj.date.strftime('%Y-%m-%d')
+ result += obj.text
return result
+
class Flatboob(ReplApplication):
APPNAME = 'flatboob'
VERSION = '0.c'
@@ -148,9 +137,9 @@ class Flatboob(ReplApplication):
query.nb_rooms = self.ask_int('Enter number of rooms')
self.change_path([u'housings'])
+ self.start_format()
for backend, housing in self.do('search_housings', query):
- self.add_object(housing)
- self.format(housing)
+ self.cached_format(housing)
self.flush()
def ask_int(self, txt):
@@ -173,5 +162,7 @@ class Flatboob(ReplApplication):
if not housing:
print >>sys.stderr, 'Housing not found: %s' % _id
return 3
+
+ self.start_format()
self.format(housing)
self.flush()
diff --git a/weboob/applications/galleroob/galleroob.py b/weboob/applications/galleroob/galleroob.py
index 9e3ab32e..7a0e9d04 100644
--- a/weboob/applications/galleroob/galleroob.py
+++ b/weboob/applications/galleroob/galleroob.py
@@ -24,33 +24,26 @@ import os
from re import search, sub
from weboob.tools.application.repl import ReplApplication
-from weboob.capabilities.base import NotLoaded
+from weboob.capabilities.base import empty
from weboob.capabilities.gallery import ICapGallery, BaseGallery, BaseImage
-from weboob.tools.application.formatters.iformatter import IFormatter
+from weboob.tools.application.formatters.iformatter import PrettyFormatter
__all__ = ['Galleroob']
-class GalleryListFormatter(IFormatter):
+class GalleryListFormatter(PrettyFormatter):
MANDATORY_FIELDS = ('id', 'title')
- count = 0
+ def get_title(self, obj):
+ s = obj.title
+ if hasattr(obj, 'cardinality') and not empty(obj.cardinality):
+ s += u' (%d pages)' % obj.cardinality
+ return s
- def flush(self):
- self.count = 0
-
- def format_dict(self, item):
- result = u'%s* (%s) %s%s' % (
- ReplApplication.BOLD,
- item['id'],
- item['title'],
- ReplApplication.NC)
- if item['cardinality'] is not NotLoaded:
- result += u' (%d pages)' % item['cardinality']
- if item['description'] is not NotLoaded:
- result += u'\n %-70s' % item['description']
- return result
+ def get_description(self, obj):
+ if hasattr(obj, 'description') and obj.description:
+ return obj.description
class Galleroob(ReplApplication):
@@ -76,11 +69,11 @@ class Galleroob(ReplApplication):
print >>sys.stderr, 'This command takes an argument: %s' % self.get_command_help('search', short=True)
return 2
- self.set_formatter_header(u'Search pattern: %s' % pattern)
- for backend, gallery in self.do('search_gallery',
- pattern=pattern, max_results=self.options.count):
- self.add_object(gallery)
- self.format(gallery)
+ self.start_format(pattern=pattern)
+ for backend, gallery in self.do('search_gallery', pattern=pattern,
+ max_results=self.options.count):
+ self.cached_format(gallery)
+ self.flush()
def do_download(self, line):
"""
@@ -159,5 +152,7 @@ class Galleroob(ReplApplication):
if not gallery:
print >>sys.stderr, 'Gallery not found: %s' % _id
return 3
+
+ self.start_format()
self.format(gallery)
self.flush()
diff --git a/weboob/applications/radioob/radioob.py b/weboob/applications/radioob/radioob.py
index 708445ab..cde7a56a 100644
--- a/weboob/applications/radioob/radioob.py
+++ b/weboob/applications/radioob/radioob.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright(C) 2010-2011 Romain Bignon
+# Copyright(C) 2010-2012 Romain Bignon
#
# This file is part of weboob.
#
@@ -21,44 +21,36 @@
import sys
from weboob.capabilities.radio import ICapRadio, Radio
-from weboob.capabilities.base import NotLoaded
+from weboob.capabilities.base import empty
from weboob.tools.application.repl import ReplApplication
from weboob.tools.application.media_player import InvalidMediaPlayer, MediaPlayer, MediaPlayerNotFound
-from weboob.tools.application.formatters.iformatter import IFormatter
+from weboob.tools.application.formatters.iformatter import PrettyFormatter
__all__ = ['Radioob']
-class RadioListFormatter(IFormatter):
+class RadioListFormatter(PrettyFormatter):
MANDATORY_FIELDS = ('id', 'title', 'description')
- count = 0
- def flush(self):
- self.count = 0
- pass
+ def get_title(self, obj):
+ return obj.title
- def format_dict(self, item):
- self.count += 1
- if self.interactive:
- backend = item['id'].split('@', 1)[1]
- result = u'%s* (%d) %s (%s)%s\n' % (ReplApplication.BOLD, self.count, item['title'], backend, ReplApplication.NC)
- else:
- result = u'%s* (%s) %s%s\n' % (ReplApplication.BOLD, item['id'], item['title'], ReplApplication.NC)
- result += ' %-30s' % item['description']
- if item['current'] is not NotLoaded:
- if item['current'].artist:
- result += ' (Current: %s - %s)' % (item['current'].artist, item['current'].title)
+ def get_description(self, obj):
+ result = '%-30s' % obj.description
+ if hasattr(obj, 'current') and not empty(obj.current):
+ if obj.current.artist:
+ result += ' (Current: %s - %s)' % (obj.current.artist, obj.current.title)
else:
- result += ' (Current: %s)' % item['current'].title
+ result += ' (Current: %s)' % obj.current.title
return result
class Radioob(ReplApplication):
APPNAME = 'radioob'
VERSION = '0.c'
- COPYRIGHT = 'Copyright(C) 2010-2011 Romain Bignon'
+ COPYRIGHT = 'Copyright(C) 2010-2012 Romain Bignon'
DESCRIPTION = 'Console application allowing to search for web radio stations, listen to them and get information ' \
'like the current song.'
CAPS = ICapRadio
diff --git a/weboob/applications/translaboob/translaboob.py b/weboob/applications/translaboob/translaboob.py
index 037f8e8b..c2501c28 100644
--- a/weboob/applications/translaboob/translaboob.py
+++ b/weboob/applications/translaboob/translaboob.py
@@ -29,23 +29,18 @@ __all__ = ['Translaboob']
class TranslationFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'text')
- def flush(self):
- pass
-
- def format_dict(self, item):
- backend = item['id'].split('@', 1)[1]
- result = u'%s* %s%s\n\t%s' % (self.BOLD, backend, self.NC, item['text'].replace('\n', '\n\t'))
- return result
+ def format_obj(self, obj, alias):
+ return u'%s* %s%s\n\t%s' % (self.BOLD, obj.backend, self.NC, obj.text.replace('\n', '\n\t'))
class XmlTranslationFormatter(IFormatter):
- MANDATORY_FIELDS = ('id', 'lang_src', 'lang_dst', 'text')
+ MANDATORY_FIELDS = ('id', 'text')
- def flush(self):
- pass
+ def start_format(self, **kwargs):
+ if 'source' in kwargs:
+ self.output('\n%s\n' % kwargs['source'])
- def format_dict(self, item):
- backend = item['id'].split('@', 1)[1]
- return u'\n%s\n' % (backend, item['text'])
+ def format_obj(self, obj, alias):
+ return u'\n%s\n' % (obj.backend, obj.text)
class Translaboob(ReplApplication):
APPNAME = 'translaboob'
@@ -74,5 +69,7 @@ class Translaboob(ReplApplication):
if not text or text == '-':
text = self.acquire_input()
+ self.start_format(source=text)
for backend, translation in self.do('translate', lan_from, lan_to, text):
self.format(translation)
+ self.flush()
diff --git a/weboob/applications/videoob/videoob.py b/weboob/applications/videoob/videoob.py
index 75f9fe10..4c8cafc1 100644
--- a/weboob/applications/videoob/videoob.py
+++ b/weboob/applications/videoob/videoob.py
@@ -27,35 +27,25 @@ from weboob.capabilities.video import ICapVideo, BaseVideo
from weboob.capabilities.base import empty
from weboob.tools.application.repl import ReplApplication
from weboob.tools.application.media_player import InvalidMediaPlayer, MediaPlayer, MediaPlayerNotFound
-from weboob.tools.application.formatters.iformatter import IFormatter
+from weboob.tools.application.formatters.iformatter import PrettyFormatter
__all__ = ['Videoob']
-class VideoListFormatter(IFormatter):
+class VideoListFormatter(PrettyFormatter):
MANDATORY_FIELDS = ('id', 'title', 'duration', 'date')
- count = 0
+ def get_title(self, obj):
+ return obj.title
- def flush(self):
- self.count = 0
- pass
-
- def format_dict(self, item):
- self.count += 1
- if self.interactive:
- backend = item['id'].split('@', 1)[1]
- result = u'%s* (%d) %s (%s)%s\n' % (self.BOLD, self.count, item['title'], backend, self.NC)
- else:
- result = u'%s* (%s) %s%s\n' % (self.BOLD, item['id'], item['title'], self.NC)
- result += ' %s' % (item['duration'] if item['duration'] else item['date'])
- if not empty(item['author']):
- result += ' - %s' % item['author']
- if not empty(item['rating']):
- result += u' (%s/%s)' % (item['rating'], item['rating_max'])
+ def get_description(self, obj):
+ result = '%s' % (obj.duration or obj.date)
+ if hasattr(obj, 'author') and not empty(obj.author):
+ result += u' - %s' % obj.author
+ if hasattr(obj, 'rating') and not empty(obj.rating):
+ result += u' (%s/%s)' % (obj.rating, obj.rating_max)
return result
-
class Videoob(ReplApplication):
APPNAME = 'videoob'
VERSION = '0.c'
@@ -180,6 +170,8 @@ class Videoob(ReplApplication):
if not video:
print >>sys.stderr, 'Video not found: %s' % _id
return 3
+
+ self.start_format()
self.format(video)
self.flush()
@@ -216,10 +208,9 @@ class Videoob(ReplApplication):
print >>sys.stderr, 'This command takes an argument: %s' % self.get_command_help('search', short=True)
return 2
- self.set_formatter_header(u'Search pattern: %s' % pattern)
self.change_path([u'search'])
+ self.start_format(pattern=pattern)
for backend, video in self.do('search_videos', pattern=pattern, nsfw=self.nsfw,
max_results=self.options.count):
- self.add_object(video)
- self.format(video)
+ self.cached_format(video)
self.flush()
diff --git a/weboob/applications/webcontentedit/webcontentedit.py b/weboob/applications/webcontentedit/webcontentedit.py
index b13aab99..3aea129d 100644
--- a/weboob/applications/webcontentedit/webcontentedit.py
+++ b/weboob/applications/webcontentedit/webcontentedit.py
@@ -133,5 +133,8 @@ class WebContentEdit(ReplApplication):
backend_names = (backend_name,) if backend_name is not None else self.enabled_backends
_id = _id.encode('utf-8')
+
+ self.start_format()
for backend, revision in self.do('iter_revisions', _id, max_results=self.options.count, backends=backend_names):
self.format(revision)
+ self.flush()
diff --git a/weboob/applications/weboorrents/weboorrents.py b/weboob/applications/weboorrents/weboorrents.py
index 80ec501e..eb12fc98 100644
--- a/weboob/applications/weboorrents/weboorrents.py
+++ b/weboob/applications/weboorrents/weboorrents.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright(C) 2010-2011 Romain Bignon
+# Copyright(C) 2010-2012 Romain Bignon
#
# This file is part of weboob.
#
@@ -23,7 +23,7 @@ import sys
from weboob.capabilities.torrent import ICapTorrent, MagnetOnly
from weboob.tools.application.repl import ReplApplication
-from weboob.tools.application.formatters.iformatter import IFormatter
+from weboob.tools.application.formatters.iformatter import IFormatter, PrettyFormatter
from weboob.core import CallErrors
@@ -40,52 +40,39 @@ def sizeof_fmt(num):
class TorrentInfoFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'name', 'size', 'seeders', 'leechers', 'url', 'files', 'description')
- def flush(self):
- pass
-
- def format_dict(self, item):
- result = u'%s%s%s\n' % (self.BOLD, item['name'], self.NC)
- result += 'ID: %s\n' % item['id']
- result += 'Size: %s\n' % sizeof_fmt(item['size'])
- result += 'Seeders: %s\n' % item['seeders']
- result += 'Leechers: %s\n' % item['leechers']
- result += 'URL: %s\n' % item['url']
- if item['magnet']:
- result += 'Magnet URL: %s\n' % item['magnet']
- if item['files']:
+ def format_obj(self, obj, alias):
+ result = u'%s%s%s\n' % (self.BOLD, obj.name, self.NC)
+ result += 'ID: %s\n' % obj.fullid
+ result += 'Size: %s\n' % sizeof_fmt(obj.size)
+ result += 'Seeders: %s\n' % obj.seeders
+ result += 'Leechers: %s\n' % obj.leechers
+ result += 'URL: %s\n' % obj.url
+ if hasattr(obj, 'magnet') and obj.magnet:
+ result += 'Magnet URL: %s\n' % obj.magnet
+ if obj.files:
result += '\n%sFiles%s\n' % (self.BOLD, self.NC)
- for f in item['files']:
+ for f in obj.files:
result += ' * %s\n' % f
result += '\n%sDescription%s\n' % (self.BOLD, self.NC)
- result += item['description']
+ result += obj.description
return result
-class TorrentListFormatter(IFormatter):
+class TorrentListFormatter(PrettyFormatter):
MANDATORY_FIELDS = ('id', 'name', 'size', 'seeders', 'leechers')
- count = 0
+ def get_title(self, obj):
+ return obj.name
- def flush(self):
- self.count = 0
- pass
-
- def format_dict(self, item):
- self.count += 1
- if self.interactive:
- backend = item['id'].split('@', 1)[1]
- result = u'%s* (%d) %s (%s)%s\n' % (self.BOLD, self.count, item['name'], backend, self.NC)
- else:
- result = u'%s* (%s) %s%s\n' % (self.BOLD, item['id'], item['name'], self.NC)
- size = sizeof_fmt(item['size'])
- result += ' %10s (Seed: %2d / Leech: %2d)' % (size, item['seeders'], item['leechers'])
- return result
+ def get_description(self, obj):
+ size = sizeof_fmt(obj.size)
+ return '%10s (Seed: %2d / Leech: %2d)' % (size, obj.seeders, obj.leechers)
class Weboorrents(ReplApplication):
APPNAME = 'weboorrents'
VERSION = '0.c'
- COPYRIGHT = 'Copyright(C) 2010-2011 Romain Bignon'
+ COPYRIGHT = 'Copyright(C) 2010-2012 Romain Bignon'
DESCRIPTION = 'Console application allowing to search for torrents on various trackers ' \
'and download .torrent files.'
CAPS = ICapTorrent
@@ -107,19 +94,15 @@ class Weboorrents(ReplApplication):
Get information about a torrent.
"""
- _id, backend_name = self.parse_id(id)
- found = 0
- for backend, torrent in self.do('get_torrent', _id, backends=backend_name):
- if torrent:
- self.format(torrent)
- found = 1
-
- if not found:
- print >>sys.stderr, 'Torrent "%s" not found' % id
+ torrent = self.get_object(id, 'get_torrent')
+ if not torrent:
+ print >>sys.stderr, 'Torrent not found: %s' % id
return 3
- else:
- self.flush()
+
+ self.start_format()
+ self.format(torrent)
+ self.flush()
def complete_getfile(self, text, line, *ignored):
args = line.split(' ', 2)
@@ -178,8 +161,8 @@ class Weboorrents(ReplApplication):
self.change_path([u'search'])
if not pattern:
pattern = None
- self.set_formatter_header(u'Search pattern: %s' % pattern if pattern else u'Latest torrents')
+
+ self.start_format(pattern=pattern)
for backend, torrent in self.do('iter_torrents', pattern=pattern):
- self.add_object(torrent)
- self.format(torrent)
+ self.cached_format(torrent)
self.flush()
diff --git a/weboob/applications/wetboobs/wetboobs.py b/weboob/applications/wetboobs/wetboobs.py
index 46d1eed0..c6432056 100644
--- a/weboob/applications/wetboobs/wetboobs.py
+++ b/weboob/applications/wetboobs/wetboobs.py
@@ -17,62 +17,43 @@
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see .
-from datetime import datetime
from weboob.capabilities.weather import ICapWeather
from weboob.capabilities.gauge import ICapWaterLevel
from weboob.tools.application.repl import ReplApplication
-from weboob.tools.application.formatters.iformatter import IFormatter
+from weboob.tools.application.formatters.iformatter import IFormatter, PrettyFormatter
__all__ = ['WetBoobs']
+
class ForecastsFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'date', 'low', 'high', 'unit')
- def flush(self):
- pass
-
- def format_dict(self, item):
- result = u'%s* %-15s%s (%s°%s - %s°%s)' % (self.BOLD, '%s:' % item['date'], self.NC, item['low'], item['unit'], item['high'], item['unit'])
- if 'text' in item and item['text']:
- result += ' %s' % item['text']
+ def format_obj(self, obj, alias):
+ result = u'%s* %-15s%s (%s°%s - %s°%s)' % (self.BOLD, '%s:' % obj.date, self.NC, obj.low, obj.unit, obj.high, obj.unit)
+ if hasattr(obj, 'text') and obj.text:
+ result += ' %s' % obj.text
return result
+
class CurrentFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'date', 'temp')
- def flush(self):
- pass
-
- def format_dict(self, item):
- if isinstance(item['date'], datetime):
- date = item['date'].strftime('%y-%m-%d %H:%M:%S')
- else:
- date = item['date']
-
- result = u'%s%s%s: %s' % (self.BOLD, date, self.NC, item['temp'])
- if 'unit' in item and item['unit']:
- result += u'°%s' % item['unit']
- if 'text' in item and item['text']:
- result += u' - %s' % item['text']
+ def format_obj(self, obj, alias):
+ result = u'%s%s%s: %s' % (self.BOLD, obj.date, self.NC, obj.temp)
+ if hasattr(obj, 'unit') and obj.unit:
+ result += u'°%s' % obj.unit
+ if hasattr(obj, 'text') and obj.text:
+ result += u' - %s' % obj.text
return result
-class CitiesFormatter(IFormatter):
+class CitiesFormatter(PrettyFormatter):
MANDATORY_FIELDS = ('id', 'name')
- count = 0
- def flush(self):
- self.count = 0
+ def get_title(self, obj):
+ return obj.name
- def format_dict(self, item):
- self.count += 1
- if self.interactive:
- backend = item['id'].split('@', 1)[1]
- result = u'%s* (%d) %s (%s)%s' % (self.BOLD, self.count, item['name'], backend, self.NC)
- else:
- result = u'%s* (%s) %s%s' % (self.BOLD, item['id'], item['name'], self.NC)
- return result
class WetBoobs(ReplApplication):
APPNAME = 'wetboobs'
@@ -97,9 +78,9 @@ class WetBoobs(ReplApplication):
Search cities.
"""
self.change_path(['cities'])
+ self.start_format()
for backend, city in self.do('iter_city_search', pattern, caps=ICapWeather):
- self.add_object(city)
- self.format(city)
+ self.cached_format(city)
self.flush()
def complete_current(self, text, line, *ignored):
@@ -115,6 +96,8 @@ class WetBoobs(ReplApplication):
"""
city, = self.parse_command_args(line, 1, 1)
_id, backend_name = self.parse_id(city)
+
+ self.start_format()
for backend, current in self.do('get_current', _id, backends=backend_name, caps=ICapWeather):
if current:
self.format(current)
@@ -133,6 +116,8 @@ class WetBoobs(ReplApplication):
"""
city, = self.parse_command_args(line, 1, 1)
_id, backend_name = self.parse_id(city)
+
+ self.start_format()
for backend, forecast in self.do('iter_forecast', _id, backends=backend_name, caps=ICapWeather):
self.format(forecast)
self.flush()
@@ -144,9 +129,9 @@ class WetBoobs(ReplApplication):
List all rivers. If PATTERN is specified, search on a pattern.
"""
self.change_path([u'gauges'])
+ self.start_format()
for backend, gauge in self.do('iter_gauges', pattern or None, caps=ICapWaterLevel):
- self.add_object(gauge)
- self.format(gauge)
+ self.cached_format(gauge)
self.flush()
def complete_gauge(self, text, line, *ignored):
@@ -162,6 +147,8 @@ class WetBoobs(ReplApplication):
"""
gauge, = self.parse_command_args(line, 1, 1)
_id, backend_name = self.parse_id(gauge)
+
+ self.start_format()
for backend, measure in self.do('iter_gauge_history', _id, backends=backend_name, caps=ICapWaterLevel):
self.format(measure)
self.flush()
@@ -179,6 +166,8 @@ class WetBoobs(ReplApplication):
"""
gauge, = self.parse_command_args(line, 1, 1)
_id, backend_name = self.parse_id(gauge)
+
+ self.start_format()
for backend, measure in self.do('get_last_measure', _id, backends=backend_name, caps=ICapWaterLevel):
self.format(measure)
self.flush()
diff --git a/weboob/tools/application/base.py b/weboob/tools/application/base.py
index cbe8133f..08d0ed9c 100644
--- a/weboob/tools/application/base.py
+++ b/weboob/tools/application/base.py
@@ -230,7 +230,7 @@ class BaseApplication(object):
return version
def _do_complete_obj(self, backend, fields, obj):
- if fields:
+ if fields is None or len(fields) > 0:
try:
backend.fillobj(obj, fields)
except ObjectNotAvailable, e:
diff --git a/weboob/tools/application/formatters/csv.py b/weboob/tools/application/formatters/csv.py
index 5ecb45e6..a091d3d8 100644
--- a/weboob/tools/application/formatters/csv.py
+++ b/weboob/tools/application/formatters/csv.py
@@ -28,15 +28,18 @@ class CSVFormatter(IFormatter):
def __init__(self, field_separator=u';'):
IFormatter.__init__(self)
self.field_separator = field_separator
- self.count = 0
+ self.started = False
def flush(self):
- pass
+ self.started = False
+
+ def format_obj(self, obj, alias):
+ item = self.to_dict(obj)
- def format_dict(self, item):
result = u''
- if self.count == 0:
+ if not self.started:
result += self.field_separator.join(item.iterkeys()) + '\n'
- self.count += 1
+ self.started = True
+
result += self.field_separator.join(unicode(v) for v in item.itervalues())
return result
diff --git a/weboob/tools/application/formatters/iformatter.py b/weboob/tools/application/formatters/iformatter.py
index bfa4f671..9d5e5acb 100644
--- a/weboob/tools/application/formatters/iformatter.py
+++ b/weboob/tools/application/formatters/iformatter.py
@@ -47,7 +47,7 @@ else:
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
-from weboob.capabilities.base import CapBaseObject, FieldNotFound
+from weboob.capabilities.base import CapBaseObject
from weboob.tools.ordereddict import OrderedDict
from weboob.tools.application.console import ConsoleApplication
@@ -78,10 +78,9 @@ class IFormatter(object):
BOLD = property(get_bold)
NC = property(get_nc)
- def __init__(self, display_keys=True, display_header=True, return_only=False, outfile=sys.stdout):
+ def __init__(self, display_keys=True, display_header=True, outfile=sys.stdout):
self.display_keys = display_keys
self.display_header = display_header
- self.return_only = return_only
self.interactive = False
self.print_lines = 0
self.termrows = 0
@@ -94,7 +93,7 @@ class IFormatter(object):
else:
self.termrows = int(subprocess.Popen('stty size', shell=True, stdout=subprocess.PIPE).communicate()[0].split()[0])
- def after_format(self, formatted):
+ def output(self, formatted):
if self.outfile != sys.stdout:
with open(self.outfile, "a+") as outfile:
outfile.write(formatted.encode('utf-8'))
@@ -113,47 +112,45 @@ class IFormatter(object):
print line
self.print_lines += 1
- def build_id(self, v, backend_name):
- return u'%s@%s' % (unicode(v), backend_name)
+ def start_format(self, **kwargs):
+ pass
def flush(self):
- raise NotImplementedError()
+ pass
- def format(self, obj, selected_fields=None):
+ def format(self, obj, selected_fields=None, alias=None):
"""
Format an object to be human-readable.
An object has fields which can be selected.
- If the object provides an iter_fields() method, the formatter will
- call it. It can be used to specify the fields order.
- @param obj [object] object to format
- @param selected_fields [tuple] fields to display. If None, all fields are selected
- @return a string of the formatted object
+ :param obj: object to format
+ :type obj: CapBaseObject
+ :param selected_fields: fields to display. If None, all fields are selected
+ :type selected_fields: tuple
+ :param alias: an alias to use instead of the object's ID
+ :type alias: unicode
"""
- assert isinstance(obj, (dict, CapBaseObject, tuple)), 'Object is unexpected type "%r"' % obj
+ assert isinstance(obj, CapBaseObject), 'Object is unexpected type "%r"' % obj
- if isinstance(obj, dict):
- item = obj
- elif isinstance(obj, tuple):
- item = OrderedDict([(k, v) for k, v in obj])
- else:
- item = self.to_dict(obj, selected_fields)
-
- if item is None:
- return None
+ if selected_fields is not None and not '*' in selected_fields:
+ obj = obj.copy()
+ for name, value in obj.iter_fields():
+ if not name in selected_fields:
+ delattr(obj, name)
if self.MANDATORY_FIELDS:
- missing_fields = set(self.MANDATORY_FIELDS) - set(item.keys())
+ missing_fields = set(self.MANDATORY_FIELDS) - set([name for name, value in obj.iter_fields()])
if missing_fields:
raise MandatoryFieldsNotFound(missing_fields)
- formatted = self.format_dict(item=item)
+ formatted = self.format_obj(obj, alias)
if formatted:
- self.after_format(formatted)
+ self.output(formatted)
return formatted
- def format_dict(self, item):
+ def format_obj(self, obj, alias=None):
"""
+ Format an object to be human-readable.
Format a dict to be human-readable. The dict is already simplified
if user provides selected fields.
Called by format().
@@ -164,31 +161,37 @@ class IFormatter(object):
"""
raise NotImplementedError()
- def set_header(self, string):
- if self.display_header:
- print string.encode('utf-8')
-
- def to_dict(self, obj, selected_fields=None):
- def iter_select(d):
- if selected_fields is None or '*' in selected_fields:
- fields = d.iterkeys()
- else:
- fields = selected_fields
-
- for key in fields:
- try:
- value = d[key]
- except KeyError:
- raise FieldNotFound(obj, key)
-
- yield key, value
-
+ def to_dict(self, obj):
def iter_decorate(d):
for key, value in d:
if key == 'id' and obj.backend is not None:
- value = self.build_id(value, obj.backend)
+ value = obj.fullid
yield key, value
fields_iterator = obj.iter_fields()
- d = OrderedDict(iter_decorate(fields_iterator))
- return OrderedDict((k, v) for k, v in iter_select(d))
+ return OrderedDict(iter_decorate(fields_iterator))
+
+
+class PrettyFormatter(IFormatter):
+ def format_obj(self, obj, alias):
+ title = self.get_title(obj)
+ desc = self.get_description(obj)
+
+ if desc is None:
+ title = '%s%s%s' % (self.NC, title, self.BOLD)
+
+ if alias is not None:
+ result = u'%s* (%s) %s (%s)%s' % (self.BOLD, alias, title, obj.backend, self.NC)
+ else:
+ result = u'%s* (%s) %s%s' % (self.BOLD, obj.fullid, title, self.NC)
+
+ if desc is not None:
+ result += u'\n\t%s' % desc
+
+ return result
+
+ def get_title(self, obj):
+ raise NotImplementedError()
+
+ def get_description(self, obj):
+ return None
diff --git a/weboob/tools/application/formatters/multiline.py b/weboob/tools/application/formatters/multiline.py
index 0d4dc779..82712174 100644
--- a/weboob/tools/application/formatters/multiline.py
+++ b/weboob/tools/application/formatters/multiline.py
@@ -34,14 +34,12 @@ class MultilineFormatter(IFormatter):
def flush(self):
pass
- def format_dict(self, item):
+ def format_obj(self, obj, alias):
+ item = self.to_dict(obj)
+
result = u'\n'.join(u'%s%s' % (
(u'%s%s' % (k, self.key_value_separator) if self.display_keys else ''), v)
for k, v in item.iteritems() if (v is not NotLoaded and v is not NotAvailable))
if len(item) > 1:
result += self.after_item
return result
-
- def set_header(self, string):
- if self.display_header:
- print string.encode('utf-8')
diff --git a/weboob/tools/application/formatters/simple.py b/weboob/tools/application/formatters/simple.py
index 9a27c536..324841d2 100644
--- a/weboob/tools/application/formatters/simple.py
+++ b/weboob/tools/application/formatters/simple.py
@@ -30,10 +30,8 @@ class SimpleFormatter(IFormatter):
self.field_separator = field_separator
self.key_value_separator = key_value_separator
- def flush(self):
- pass
-
- def format_dict(self, item):
+ def format_obj(self, obj, alias):
+ item = self.to_dict(obj)
return self.field_separator.join(u'%s%s' % (
(u'%s%s' % (k, self.key_value_separator) if self.display_keys else ''), v)
for k, v in item.iteritems())
diff --git a/weboob/tools/application/formatters/table.py b/weboob/tools/application/formatters/table.py
index 5baf5ceb..dbb707ec 100644
--- a/weboob/tools/application/formatters/table.py
+++ b/weboob/tools/application/formatters/table.py
@@ -31,18 +31,18 @@ __all__ = ['TableFormatter', 'HTMLTableFormatter']
class TableFormatter(IFormatter):
HTML = False
- def __init__(self, display_keys=True, return_only=False):
- IFormatter.__init__(self, display_keys=display_keys, return_only=return_only)
+ def __init__(self):
+ IFormatter.__init__(self)
self.queue = []
self.keys = None
self.header = None
- def after_format(self, formatted):
- if self.keys is None:
- self.keys = formatted.keys()
- self.queue.append(formatted.values())
-
def flush(self):
+ s = self.get_formatted_table()
+ if s is not None:
+ print s.encode('utf-8')
+
+ def get_formatted_table(self):
if len(self.queue) == 0:
return
@@ -80,14 +80,14 @@ class TableFormatter(IFormatter):
self.queue = []
- if self.return_only:
- return s
- else:
- print s.encode('utf-8')
+ return s
- def format_dict(self, item):
- # format is done in self.flush() by prettytable
- return item
+ def format_obj(self, obj, alias):
+ item = self.to_dict(obj)
+
+ if self.keys is None:
+ self.keys = item.keys()
+ self.queue.append(item.values())
def set_header(self, string):
self.header = string
diff --git a/weboob/tools/application/formatters/webkit/webkitgtk.py b/weboob/tools/application/formatters/webkit/webkitgtk.py
index 15d0a038..b3283f2f 100644
--- a/weboob/tools/application/formatters/webkit/webkitgtk.py
+++ b/weboob/tools/application/formatters/webkit/webkitgtk.py
@@ -43,11 +43,8 @@ class WebBrowser(gtk.Window):
class WebkitGtkFormatter(HTMLTableFormatter):
- def __init__(self):
- HTMLTableFormatter.__init__(self, return_only=True)
-
def flush(self):
- table_string = HTMLTableFormatter.flush(self)
+ table_string = self.get_formatted_table()
js_filepaths = []
js_filepaths.append(get_javascript('jquery'))
js_filepaths.append(get_javascript('tablesorter'))
diff --git a/weboob/tools/application/repl.py b/weboob/tools/application/repl.py
index 9e70c92d..99b3db46 100644
--- a/weboob/tools/application/repl.py
+++ b/weboob/tools/application/repl.py
@@ -264,9 +264,9 @@ class ReplApplication(Cmd, ConsoleApplication):
kwargs['backends'] = self.enabled_backends if backends is None else backends
kwargs['condition'] = self.condition
fields = self.selected_fields
- if fields == '$direct':
+ if '$direct' in fields:
fields = []
- elif fields == '$full':
+ elif '$full' in fields:
fields = None
return self.weboob.do(self._do_complete, self.options.count, fields, function, *args, **kwargs)
@@ -405,7 +405,7 @@ class ReplApplication(Cmd, ConsoleApplication):
if self.options.select:
self.selected_fields = self.options.select.split(',')
else:
- self.selected_fields = '$direct'
+ self.selected_fields = ['$direct']
if self.options.condition:
self.condition = ResultsCondition(self.options.condition)
@@ -800,15 +800,9 @@ class ReplApplication(Cmd, ConsoleApplication):
line = line.strip()
if line:
split = line.split()
- if len(split) == 1 and split[0] in ('$direct', '$full'):
- self.selected_fields = split[0]
- else:
- self.selected_fields = split
+ self.selected_fields = split
else:
- if isinstance(self.selected_fields, basestring):
- print self.selected_fields
- else:
- print ' '.join(self.selected_fields)
+ print ' '.join(self.selected_fields)
def complete_inspect(self, text, line, begidx, endidx):
return sorted(set(backend.name for backend in self.enabled_backends))
@@ -864,11 +858,12 @@ class ReplApplication(Cmd, ConsoleApplication):
# We have an argument, let's ch to the directory before the ls
self.working_path.cd1(path)
- self.objects, collections = self._fetch_objects(objs=self.COLLECTION_OBJECTS)
+ objects, collections = self._fetch_objects(objs=self.COLLECTION_OBJECTS)
- for obj in self.objects:
+ self.start_format()
+ for obj in objects:
if isinstance(obj, CapBaseObject):
- self.format(obj)
+ self.cached_format(obj)
else:
print obj
@@ -1012,14 +1007,24 @@ class ReplApplication(Cmd, ConsoleApplication):
return name
def set_formatter_header(self, string):
- self.formatter.set_header(string)
+ pass
- def format(self, result):
+ def start_format(self, **kwargs):
+ self.formatter.start_format(**kwargs)
+
+ def cached_format(self, obj):
+ self.add_object(obj)
+ alias = None
+ if self.interactive:
+ alias = '%s' % len(self.objects)
+ self.format(obj, alias=alias)
+
+ def format(self, result, alias=None):
fields = self.selected_fields
- if fields in ('$direct', '$full'):
+ if '$direct' in fields or '$full' in fields:
fields = None
try:
- self.formatter.format(obj=result, selected_fields=fields)
+ self.formatter.format(obj=result, selected_fields=fields, alias=alias)
except FieldNotFound, e:
print >>sys.stderr, e
except MandatoryFieldsNotFound, e: