rewrite of the formatters system

This commit is contained in:
Romain Bignon 2012-04-03 22:40:18 +02:00
commit fc849995f4
22 changed files with 441 additions and 580 deletions

View file

@ -23,7 +23,7 @@ import sys
from weboob.capabilities.bank import ICapBank, Account, Transaction from weboob.capabilities.bank import ICapBank, Account, Transaction
from weboob.tools.application.repl import ReplApplication 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'] __all__ = ['Boobank']
@ -32,22 +32,16 @@ __all__ = ['Boobank']
class QifFormatter(IFormatter): class QifFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'date', 'raw', 'amount', 'category') MANDATORY_FIELDS = ('id', 'date', 'raw', 'amount', 'category')
count = 0 def start_format(self, **kwargs):
self.output(u'!type:Bank')
def flush(self): def format_obj(self, obj, alias):
self.count = 0 result = u'D%s\n' % obj.date.strftime('%d/%m/%y')
result += u'T%s\n' % obj.amount
def format_dict(self, item): if obj.category:
result = u'' result += u'N%s\n' % obj.category
if self.count == 0: result += u'M%s\n' % obj.raw
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']
result += u'^\n' result += u'^\n'
self.count += 1
return result return result
@ -55,110 +49,78 @@ class TransactionsFormatter(IFormatter):
MANDATORY_FIELDS = ('date', 'label', 'amount') MANDATORY_FIELDS = ('date', 'label', 'amount')
TYPES = ['', 'Transfer', 'Order', 'Check', 'Deposit', 'Payback', 'Withdrawal', 'Card', 'Loan', 'Bank'] 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): def format_obj(self, obj, alias):
if self.count < 1: if hasattr(obj, 'category') and obj.category:
return _type = obj.category
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']
else: else:
try: try:
_type = self.TYPES[item['type']] _type = self.TYPES[obj.type]
except IndexError: except (IndexError,AttributeError):
_type = '' _type = ''
label = item['label'] label = obj.label
if not label: if not label and hasattr(obj, 'raw'):
label = item['raw'] label = obj.raw
result += ' %-10s %-12s %-50s %10.2f' % (item['date'].strftime('%Y-%m-%d'), _type, label[:50], item['amount']) return ' %-10s %-12s %-50s %10.2f' % (obj.date.strftime('%Y-%m-%d'), _type[:12], label[:50], obj.amount)
return result
class TransferFormatter(IFormatter): class TransferFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'date', 'origin', 'recipient', 'amount') MANDATORY_FIELDS = ('id', 'date', 'origin', 'recipient', 'amount')
def flush(self): def format_obj(self, obj, alias):
pass result = u'------- Transfer %s -------\n' % obj.fillud
result += u'Date: %s\n' % obj.date
def format_dict(self, item): result += u'Origin: %s\n' % obj.origin
result = u'------- Transfer %s -------\n' % item['id'] result += u'Recipient: %s\n' % obj.recipient
result += u'Date: %s\n' % item['date'] result += u'Amount: %.2f\n' % obj.amount
result += u'Origin: %s\n' % item['origin']
result += u'Recipient: %s\n' % item['recipient']
result += u'Amount: %.2f\n' % item['amount']
return result return result
class RecipientListFormatter(IFormatter): class RecipientListFormatter(PrettyFormatter):
MANDATORY_FIELDS = ('id', 'label') MANDATORY_FIELDS = ('id', 'label')
count = 0 def start_format(self, **kwargs):
self.output('Available recipients:')
def flush(self): def get_title(self, obj):
self.count = 0 return obj.label
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'])
class AccountListFormatter(IFormatter): class AccountListFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'label', 'balance', 'coming') MANDATORY_FIELDS = ('id', 'label', 'balance', 'coming')
count = 0
tot_balance = Decimal(0) tot_balance = Decimal(0)
tot_coming = Decimal(0) tot_coming = Decimal(0)
def flush(self): def start_format(self, **kwargs):
if self.count < 1: self.output(' %s Account Balance Coming ' % ((' ' * 15) if not self.interactive else ''))
return self.output('------------------------------------------%s+----------+----------' % (('-' * 15) if not self.interactive else ''))
result = u'------------------------------------------%s+----------+----------\n' % (('-' * 15) if not self.interactive else '') def format_obj(self, obj, alias):
result += u'%s Total %8s %8s' % ((' ' * 15) if not self.interactive else '', if alias is not None:
'%.2f' % self.tot_balance, '%.2f' % self.tot_coming) id = '#%s (%s)' % (alias, obj.backend)
self.after_format(result) 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_balance = Decimal(0)
self.tot_coming = 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): class Boobank(ReplApplication):
@ -219,6 +181,7 @@ class Boobank(ReplApplication):
account = backend.get_account(id) account = backend.get_account(id)
return backend.iter_history(account) return backend.iter_history(account)
self.start_format()
for backend, operation in self.do(do, backends=names): for backend, operation in self.do(do, backends=names):
self.format(operation) self.format(operation)
self.flush() self.flush()
@ -241,6 +204,7 @@ class Boobank(ReplApplication):
account = backend.get_account(id) account = backend.get_account(id)
return backend.iter_coming(account) return backend.iter_coming(account)
self.start_format(id=id)
for backend, operation in self.do(do, backends=names): for backend, operation in self.do(do, backends=names):
self.format(operation) self.format(operation)
self.flush() self.flush()
@ -273,9 +237,10 @@ class Boobank(ReplApplication):
self.set_formatter('recipient_list') self.set_formatter('recipient_list')
self.set_formatter_header(u'Available recipients') self.set_formatter_header(u'Available recipients')
names = (backend_name_from,) if backend_name_from is not None else None 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): for backend, recipient in self.do('iter_transfer_recipients', id_from, backends=names):
self.format(recipient) self.cached_format(recipient)
self.add_object(recipient)
self.flush() self.flush()
return 0 return 0
@ -295,6 +260,7 @@ class Boobank(ReplApplication):
names = (backend_name,) if backend_name is not None else None 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): for backend, transfer in self.do('transfer', id_from, id_to, amount, reason, backends=names):
self.format(transfer) self.format(transfer)
self.flush() self.flush()

View file

@ -22,30 +22,17 @@ import sys
from weboob.capabilities.bill import ICapBill, Detail, Subscription from weboob.capabilities.bill import ICapBill, Detail, Subscription
from weboob.tools.application.repl import ReplApplication 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'] __all__ = ['Boobill']
class SubscriptionsFormatter(IFormatter): class SubscriptionsFormatter(PrettyFormatter):
MANDATORY_FIELDS = ('id', 'label') 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): class Boobill(ReplApplication):
APPNAME = 'boobill' APPNAME = 'boobill'
@ -72,9 +59,9 @@ class Boobill(ReplApplication):
List subscriptions List subscriptions
""" """
self.start_format()
for backend, subscription in self.do('iter_subscription'): for backend, subscription in self.do('iter_subscription'):
self.add_object(subscription) self.cached_format(subscription)
self.format(subscription)
self.flush() self.flush()
def do_details(self, id): def do_details(self, id):
@ -90,15 +77,15 @@ class Boobill(ReplApplication):
return 2 return 2
names = (backend_name,) if backend_name is not None else None names = (backend_name,) if backend_name is not None else None
def do(backend): # XXX: should be generated by backend? -Flo
return backend.get_details(id) # XXX: no, but you should do it in a specific formatter -romain
# XXX: should be generated by backend?
mysum = Detail() mysum = Detail()
mysum.label = "Sum" mysum.label = "Sum"
mysum.infos = "Generated by boobill" mysum.infos = "Generated by boobill"
mysum.price = 0. 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) self.format(detail)
mysum.price = detail.price + mysum.price mysum.price = detail.price + mysum.price
@ -118,10 +105,8 @@ class Boobill(ReplApplication):
return 2 return 2
names = (backend_name,) if backend_name is not None else None names = (backend_name,) if backend_name is not None else None
def do(backend): self.start_format()
return backend.iter_history(id) for backend, history in self.do('iter_history', id, backends=names):
for backend, history in self.do(do, backends=names):
self.format(history) self.format(history)
self.flush() self.flush()
@ -129,7 +114,7 @@ class Boobill(ReplApplication):
""" """
bills Id 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 id is the identifier of the backend
""" """
@ -139,10 +124,8 @@ class Boobill(ReplApplication):
return 2 return 2
names = (backend_name,) if backend_name is not None else None names = (backend_name,) if backend_name is not None else None
def do(backend): self.start_format()
return backend.iter_bills(id) for backend, date in self.do('iter_bills', id, backends=names):
for backend, date in self.do(do, backends=names):
self.format(date) self.format(date)
self.flush() self.flush()
@ -150,7 +133,7 @@ class Boobill(ReplApplication):
""" """
download Id [FILENAME] download Id [FILENAME]
download the bill download the bill
id is the identifier of the bill (hint: try bills command) id is the identifier of the bill (hint: try bills command)
FILENAME is where to write the file. If FILENAME is '-', FILENAME is where to write the file. If FILENAME is '-',
the file is written to stdout. 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) print >>sys.stderr, 'Unable to write bill in "%s": %s' % (dest, e)
return 1 return 1
return return

View file

@ -35,50 +35,48 @@ __all__ = ['Boobmsg']
class XHtmlFormatter(IFormatter): class XHtmlFormatter(IFormatter):
def flush(self): MANDATORY_FIELDS = ('title', 'date', 'sender', 'signature', 'content')
pass
def format_dict(self, item): def format_obj(self, obj, alias):
result = "<div>\n" result = "<div>\n"
result += "<h1>%s</h1>" % (item['title']) result += "<h1>%s</h1>" % (obj.title)
result += "<dl>" result += "<dl>"
result += "<dt>Date</dt><dd>%s</dd>" % (item['date']) result += "<dt>Date</dt><dd>%s</dd>" % (obj.date)
result += "<dt>Sender</dt><dd>%s</dd>" % (item['sender']) result += "<dt>Sender</dt><dd>%s</dd>" % (obj.sender)
result += "<dt>Signature</dt><dd>%s</dd>" % (item['signature']) result += "<dt>Signature</dt><dd>%s</dd>" % (obj.signature)
result += "</dl>" result += "</dl>"
result += "<div>%s</div>" % (item['content']) result += "<div>%s</div>" % (obj.content)
result += "</div>\n" result += "</div>\n"
return result return result
class MessageFormatter(IFormatter): class MessageFormatter(IFormatter):
def flush(self): MANDATORY_FIELDS = ('title', 'date', 'sender', 'signature', 'content')
pass
def format_dict(self, item): def format_obj(self, obj, alias):
result = u'%sTitle:%s %s\n' % (self.BOLD, result = u'%sTitle:%s %s\n' % (self.BOLD,
self.NC, item['title']) self.NC, obj.title)
result += u'%sDate:%s %s\n' % (self.BOLD, result += u'%sDate:%s %s\n' % (self.BOLD,
self.NC, item['date']) self.NC, obj.date)
result += u'%sFrom:%s %s\n' % (self.BOLD, result += u'%sFrom:%s %s\n' % (self.BOLD,
self.NC, item['sender']) self.NC, obj.sender)
if item['receivers']: if hasattr(obj, 'receivers') and obj.receivers:
result += u'%sTo:%s %s\n' % (self.BOLD, result += u'%sTo:%s %s\n' % (self.BOLD,
self.NC, self.NC,
', '.join(item['receivers'])) ', '.join(obj.receivers))
if item['flags'] & Message.IS_HTML: if obj.flags & Message.IS_HTML:
content = html2text(item['content']) content = html2text(obj.content)
else: else:
content = item['content'] content = obj.content
result += '\n%s' % content result += '\n%s' % content
if item['signature']: if obj.signature:
if item['flags'] & Message.IS_HTML: if obj.flags & Message.IS_HTML:
signature = html2text(item['signature']) signature = html2text(obj.signature)
else: else:
signature = item['signature'] signature = obj.signature
result += '\n-- \n%s' % signature result += '\n-- \n%s' % signature
return result return result
@ -92,36 +90,34 @@ class MessagesListFormatter(IFormatter):
def flush(self): def flush(self):
self.count = 0 self.count = 0
def format_dict(self, item): def format_obj(self, obj, alias):
if not self._list_messages: if not self._list_messages:
return self.format_dict_thread(item) return self.format_dict_thread(obj, alias)
else: 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 self.count += 1
if self.interactive: if self.interactive:
backend = item['id'].split('@', 1)[1]
result = u'%s* (%d) %s (%s)%s' % (self.BOLD, result = u'%s* (%d) %s (%s)%s' % (self.BOLD,
self.count, self.count,
item['title'], backend, obj.title, obj.backend,
self.NC) self.NC)
else: else:
result = u'%s* (%s) %s%s' % (self.BOLD, item['id'], result = u'%s* (%s) %s%s' % (self.BOLD, obj.id,
item['title'], obj.title,
self.NC) self.NC)
if item['date']: if obj.date:
result += u'\n %s' % item['date'] result += u'\n %s' % obj.date
return result return result
def format_dict_messages(self, item): def format_dict_messages(self, obj, alias):
backend = item['id'].split('@', 1)[1] if obj.flags == Thread.IS_THREADS:
if item['flags'] == Thread.IS_THREADS:
depth = 0 depth = 0
else: else:
depth = -1 depth = -1
result = self.format_message(backend, item['root'], depth) result = self.format_message(obj.backend, obj.root, depth)
return result return result
def format_message(self, backend, message, depth=0): 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) result += u'\t' * level + u'%-20s %s\n' % (node.label + ':', value)
return result return result
def format_dict(self, item): def format_obj(self, obj):
result = u'Nickname: %s\n' % item['name'] result = u'Nickname: %s\n' % obj.name
if item['status'] & Contact.STATUS_ONLINE: if obj.status & Contact.STATUS_ONLINE:
s = 'online' s = 'online'
elif item['status'] & Contact.STATUS_OFFLINE: elif obj.status & Contact.STATUS_OFFLINE:
s = 'offline' s = 'offline'
elif item['status'] & Contact.STATUS_AWAY: elif obj.status & Contact.STATUS_AWAY:
s = 'away' s = 'away'
else: else:
s = 'unknown' 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' 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'\t%s%s\n' % (photo, ' (hidden)' if photo.hidden else '')
result += u'Profile:\n' result += u'Profile:\n'
for head in item['profile'].itervalues(): for head in obj.profile.itervalues():
result += self.print_node(head) result += self.print_node(head)
result += u'Description:\n' result += u'Description:\n'
for s in item['summary'].split('\n'): for s in obj.summary.split('\n'):
result += u'\t%s\n' % s result += u'\t%s\n' % s
return result return result
@ -439,7 +435,7 @@ class Boobmsg(ReplApplication):
def do_photos(self, id): def do_photos(self, id):
""" """
profile ID photos ID
Display photos of a profile Display photos of a profile
""" """

View file

@ -19,33 +19,20 @@
from weboob.capabilities.library import ICapBook, Book from weboob.capabilities.library import ICapBook, Book
from weboob.tools.application.repl import ReplApplication 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 import sys
__all__ = ['Boobooks'] __all__ = ['Boobooks']
class RentedListFormatter(IFormatter): class RentedListFormatter(PrettyFormatter):
MANDATORY_FIELDS = ('id', 'date', 'author', 'name', 'late') MANDATORY_FIELDS = ('id', 'date', 'author', 'name', 'late')
RED = '' RED = ''
count = 0 def get_title(self, obj):
s = u'%s%s (%s)' % (obj.author, obj.name, obj.date)
def flush(self): if obj.late:
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']:
s += u' %sLATE!%s' % (self.RED, self.NC) s += u' %sLATE!%s' % (self.RED, self.NC)
return s return s

View file

@ -23,7 +23,7 @@ import sys
from weboob.capabilities.bugtracker import ICapBugTracker, Query, Update, Project, Issue from weboob.capabilities.bugtracker import ICapBugTracker, Query, Update, Project, Issue
from weboob.tools.application.repl import ReplApplication 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 from weboob.tools.misc import html2text
@ -33,28 +33,25 @@ __all__ = ['BoobTracker']
class IssueFormatter(IFormatter): class IssueFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'project', 'title', 'body', 'author') MANDATORY_FIELDS = ('id', 'project', 'title', 'body', 'author')
def flush(self): def format_obj(self, obj, alias):
pass 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
def format_dict(self, item): result += 'Author: %s (%s)\n' % (obj.author.name, obj.creation)
result = u'%s%s - #%s - %s%s\n' % (self.BOLD, item['project'].name, item['id'], item['title'], self.NC) if hasattr(obj, 'status') and obj.status:
result += '\n%s\n\n' % item['body'] result += 'Status: %s\n' % obj.status.name
result += 'Author: %s (%s)\n' % (item['author'].name, item['creation']) if hasattr(obj, 'version') and obj.version:
if item['status']: result += 'Version: %s\n' % obj.version.name
result += 'Status: %s\n' % item['status'].name if hasattr(obj, 'category') and obj.category:
if item['version']: result += 'Category: %s\n' % obj.category
result += 'Version: %s\n' % item['version'].name if hasattr(obj, 'assignee') and obj.assignee:
if item['category']: result += 'Assignee: %s\n' % (obj.assignee.name)
result += 'Category: %s\n' % item['category'] if hasattr(obj, 'attachments') and obj.attachments:
if item['assignee']:
result += 'Assignee: %s\n' % (item['assignee'].name)
if item['attachments']:
result += '\nAttachments:\n' 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) 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' 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) result += '* %s%s - %s%s\n' % (self.BOLD, u.date, u.author.name, self.NC)
for change in u.changes: for change in u.changes:
result += ' - %s%s%s: %s -> %s\n' % (self.BOLD, change.field, self.NC, change.last, change.new) 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) result += html2text(u.message)
return result return result
class IssuesListFormatter(IFormatter): class IssuesListFormatter(PrettyFormatter):
MANDATORY_FIELDS = ('id', 'project', 'status', 'title', 'category') 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): def get_description(self, obj):
self.count = 0 return obj.category
pass
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): class BoobTracker(ReplApplication):
APPNAME = 'boobtracker' APPNAME = 'boobtracker'

View file

@ -25,7 +25,7 @@ import sys
from weboob.capabilities.pricecomparison import ICapPriceComparison from weboob.capabilities.pricecomparison import ICapPriceComparison
from weboob.tools.misc import html2text from weboob.tools.misc import html2text
from weboob.tools.application.repl import ReplApplication 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'] __all__ = ['Comparoob']
@ -34,55 +34,46 @@ __all__ = ['Comparoob']
class PriceFormatter(IFormatter): class PriceFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'cost', 'currency', 'shop', 'product') MANDATORY_FIELDS = ('id', 'cost', 'currency', 'shop', 'product')
def flush(self): def format_obj(self, obj, alias):
pass if hasattr(obj, 'message') and obj.message:
message = obj.message
def format_dict(self, item):
if item['message']:
message = item['message']
else: 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 = u'%s%s%s\n' % (self.BOLD, message, self.NC)
result += 'ID: %s\n' % item['id'] result += u'ID: %s\n' % obj.fullid
result += 'Product: %s\n' % item['product'].name result += u'Product: %s\n' % obj.product.name
result += 'Cost: %s%s\n' % (item['cost'], item['currency']) result += u'Cost: %s%s\n' % (obj.cost, obj.currency)
if item['date']: if hasattr(obj, 'date') and obj.date:
result += 'Date: %s\n' % item['date'].strftime('%Y-%m-%d') result += u'Date: %s\n' % obj.date.strftime('%Y-%m-%d')
result += '\n%sShop:%s\n' % (self.BOLD, self.NC) result += u'\n%sShop:%s\n' % (self.BOLD, self.NC)
result += '\tName: %s\n' % item['shop'].name result += u'\tName: %s\n' % obj.shop.name
if item['shop'].location: if obj.shop.location:
result += '\tLocation: %s\n' % item['shop'].location result += u'\tLocation: %s\n' % obj.shop.location
if item['shop'].info: if obj.shop.info:
result += '\n\t' + html2text(item['shop'].info).replace('\n', '\n\t').strip() result += u'\n\t' + html2text(obj.shop.info).replace('\n', '\n\t').strip()
return result return result
class PricesFormatter(IFormatter): class PricesFormatter(PrettyFormatter):
MANDATORY_FIELDS = ('id', 'cost', 'currency', 'shop') MANDATORY_FIELDS = ('id', 'cost', 'currency')
count = 0 def get_title(self, obj):
if hasattr(obj, 'message') and obj.message:
def flush(self): message = obj.message
self.count = 0 elif hasattr(obj, 'shop') and obj.shop:
message = '%s (%s)' % (obj.shop.name, obj.shop.location)
def format_dict(self, item):
self.count += 1
if item['message']:
message = item['message']
else: 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): class Comparoob(ReplApplication):
APPNAME = 'comparoob' APPNAME = 'comparoob'
@ -113,9 +104,10 @@ class Comparoob(ReplApplication):
return 1 return 1
self.change_path([u'prices']) self.change_path([u'prices'])
self.start_format()
for backend, price in self.do('iter_prices', product): for backend, price in self.do('iter_prices', product):
self.add_object(price) self.cached_format(price)
self.format(price) self.flush()
def complete_info(self, text, line, *ignored): def complete_info(self, text, line, *ignored):
args = line.split(' ') args = line.split(' ')
@ -131,5 +123,7 @@ class Comparoob(ReplApplication):
if not price: if not price:
print >>sys.stderr, 'Price not found: %s' % _id print >>sys.stderr, 'Price not found: %s' % _id
return 3 return 3
self.start_format()
self.format(price) self.format(price)
self.flush() self.flush()

View file

@ -22,7 +22,7 @@ import sys
from weboob.capabilities.housing import ICapHousing, Query from weboob.capabilities.housing import ICapHousing, Query
from weboob.tools.application.repl import ReplApplication 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'] __all__ = ['Flatboob']
@ -31,58 +31,47 @@ __all__ = ['Flatboob']
class HousingFormatter(IFormatter): class HousingFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'title', 'cost', 'currency', 'area', 'date', 'text') MANDATORY_FIELDS = ('id', 'title', 'cost', 'currency', 'area', 'date', 'text')
def flush(self): def format_obj(self, obj, alias):
pass 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: %s\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): if hasattr(obj, 'photos') and obj.photos:
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: %s\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']:
result += '\n%sPhotos%s\n' % (self.BOLD, self.NC) 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 += ' * %s\n' % photo.url
result += '\n%sDescription%s\n' % (self.BOLD, self.NC) 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) 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) result += ' %s: %s\n' % (key, value)
return result return result
class HousingListFormatter(IFormatter): class HousingListFormatter(PrettyFormatter):
MANDATORY_FIELDS = ('id', 'title', 'cost', 'text') 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): def get_description(self, obj):
self.count = 0 result = u''
pass if hasattr(obj, 'date') and obj.date:
result += '%s - ' % obj.date.strftime('%Y-%m-%d')
def format_dict(self, item): result += obj.text
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']
return result return result
class Flatboob(ReplApplication): class Flatboob(ReplApplication):
APPNAME = 'flatboob' APPNAME = 'flatboob'
VERSION = '0.c' VERSION = '0.c'
@ -148,9 +137,9 @@ class Flatboob(ReplApplication):
query.nb_rooms = self.ask_int('Enter number of rooms') query.nb_rooms = self.ask_int('Enter number of rooms')
self.change_path([u'housings']) self.change_path([u'housings'])
self.start_format()
for backend, housing in self.do('search_housings', query): for backend, housing in self.do('search_housings', query):
self.add_object(housing) self.cached_format(housing)
self.format(housing)
self.flush() self.flush()
def ask_int(self, txt): def ask_int(self, txt):
@ -173,5 +162,7 @@ class Flatboob(ReplApplication):
if not housing: if not housing:
print >>sys.stderr, 'Housing not found: %s' % _id print >>sys.stderr, 'Housing not found: %s' % _id
return 3 return 3
self.start_format()
self.format(housing) self.format(housing)
self.flush() self.flush()

View file

@ -24,33 +24,26 @@ import os
from re import search, sub from re import search, sub
from weboob.tools.application.repl import ReplApplication 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.capabilities.gallery import ICapGallery, BaseGallery, BaseImage
from weboob.tools.application.formatters.iformatter import IFormatter from weboob.tools.application.formatters.iformatter import PrettyFormatter
__all__ = ['Galleroob'] __all__ = ['Galleroob']
class GalleryListFormatter(IFormatter): class GalleryListFormatter(PrettyFormatter):
MANDATORY_FIELDS = ('id', 'title') 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): def get_description(self, obj):
self.count = 0 if hasattr(obj, 'description') and obj.description:
return obj.description
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
class Galleroob(ReplApplication): 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) print >>sys.stderr, 'This command takes an argument: %s' % self.get_command_help('search', short=True)
return 2 return 2
self.set_formatter_header(u'Search pattern: %s' % pattern) self.start_format(pattern=pattern)
for backend, gallery in self.do('search_gallery', for backend, gallery in self.do('search_gallery', pattern=pattern,
pattern=pattern, max_results=self.options.count): max_results=self.options.count):
self.add_object(gallery) self.cached_format(gallery)
self.format(gallery) self.flush()
def do_download(self, line): def do_download(self, line):
""" """
@ -159,5 +152,7 @@ class Galleroob(ReplApplication):
if not gallery: if not gallery:
print >>sys.stderr, 'Gallery not found: %s' % _id print >>sys.stderr, 'Gallery not found: %s' % _id
return 3 return 3
self.start_format()
self.format(gallery) self.format(gallery)
self.flush() self.flush()

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright(C) 2010-2011 Romain Bignon # Copyright(C) 2010-2012 Romain Bignon
# #
# This file is part of weboob. # This file is part of weboob.
# #
@ -21,44 +21,36 @@
import sys import sys
from weboob.capabilities.radio import ICapRadio, Radio 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.repl import ReplApplication
from weboob.tools.application.media_player import InvalidMediaPlayer, MediaPlayer, MediaPlayerNotFound 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'] __all__ = ['Radioob']
class RadioListFormatter(IFormatter): class RadioListFormatter(PrettyFormatter):
MANDATORY_FIELDS = ('id', 'title', 'description') MANDATORY_FIELDS = ('id', 'title', 'description')
count = 0
def flush(self): def get_title(self, obj):
self.count = 0 return obj.title
pass
def format_dict(self, item): def get_description(self, obj):
self.count += 1 result = '%-30s' % obj.description
if self.interactive: if hasattr(obj, 'current') and not empty(obj.current):
backend = item['id'].split('@', 1)[1] if obj.current.artist:
result = u'%s* (%d) %s (%s)%s\n' % (ReplApplication.BOLD, self.count, item['title'], backend, ReplApplication.NC) result += ' (Current: %s - %s)' % (obj.current.artist, obj.current.title)
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)
else: else:
result += ' (Current: %s)' % item['current'].title result += ' (Current: %s)' % obj.current.title
return result return result
class Radioob(ReplApplication): class Radioob(ReplApplication):
APPNAME = 'radioob' APPNAME = 'radioob'
VERSION = '0.c' 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 ' \ DESCRIPTION = 'Console application allowing to search for web radio stations, listen to them and get information ' \
'like the current song.' 'like the current song.'
CAPS = ICapRadio CAPS = ICapRadio

View file

@ -29,23 +29,18 @@ __all__ = ['Translaboob']
class TranslationFormatter(IFormatter): class TranslationFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'text') MANDATORY_FIELDS = ('id', 'text')
def flush(self): def format_obj(self, obj, alias):
pass return u'%s* %s%s\n\t%s' % (self.BOLD, obj.backend, self.NC, obj.text.replace('\n', '\n\t'))
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
class XmlTranslationFormatter(IFormatter): class XmlTranslationFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'lang_src', 'lang_dst', 'text') MANDATORY_FIELDS = ('id', 'text')
def flush(self): def start_format(self, **kwargs):
pass if 'source' in kwargs:
self.output('<source>\n%s\n</source>' % kwargs['source'])
def format_dict(self, item): def format_obj(self, obj, alias):
backend = item['id'].split('@', 1)[1] return u'<translation %s>\n%s\n</translation>' % (obj.backend, obj.text)
return u'<translation %s>\n%s\n</translation>' % (backend, item['text'])
class Translaboob(ReplApplication): class Translaboob(ReplApplication):
APPNAME = 'translaboob' APPNAME = 'translaboob'
@ -74,5 +69,7 @@ class Translaboob(ReplApplication):
if not text or text == '-': if not text or text == '-':
text = self.acquire_input() text = self.acquire_input()
self.start_format(source=text)
for backend, translation in self.do('translate', lan_from, lan_to, text): for backend, translation in self.do('translate', lan_from, lan_to, text):
self.format(translation) self.format(translation)
self.flush()

View file

@ -27,35 +27,25 @@ from weboob.capabilities.video import ICapVideo, BaseVideo
from weboob.capabilities.base import empty from weboob.capabilities.base import empty
from weboob.tools.application.repl import ReplApplication from weboob.tools.application.repl import ReplApplication
from weboob.tools.application.media_player import InvalidMediaPlayer, MediaPlayer, MediaPlayerNotFound 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'] __all__ = ['Videoob']
class VideoListFormatter(IFormatter): class VideoListFormatter(PrettyFormatter):
MANDATORY_FIELDS = ('id', 'title', 'duration', 'date') MANDATORY_FIELDS = ('id', 'title', 'duration', 'date')
count = 0 def get_title(self, obj):
return obj.title
def flush(self): def get_description(self, obj):
self.count = 0 result = '%s' % (obj.duration or obj.date)
pass if hasattr(obj, 'author') and not empty(obj.author):
result += u' - %s' % obj.author
def format_dict(self, item): if hasattr(obj, 'rating') and not empty(obj.rating):
self.count += 1 result += u' (%s/%s)' % (obj.rating, obj.rating_max)
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'])
return result return result
class Videoob(ReplApplication): class Videoob(ReplApplication):
APPNAME = 'videoob' APPNAME = 'videoob'
VERSION = '0.c' VERSION = '0.c'
@ -180,6 +170,8 @@ class Videoob(ReplApplication):
if not video: if not video:
print >>sys.stderr, 'Video not found: %s' % _id print >>sys.stderr, 'Video not found: %s' % _id
return 3 return 3
self.start_format()
self.format(video) self.format(video)
self.flush() 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) print >>sys.stderr, 'This command takes an argument: %s' % self.get_command_help('search', short=True)
return 2 return 2
self.set_formatter_header(u'Search pattern: %s' % pattern)
self.change_path([u'search']) self.change_path([u'search'])
self.start_format(pattern=pattern)
for backend, video in self.do('search_videos', pattern=pattern, nsfw=self.nsfw, for backend, video in self.do('search_videos', pattern=pattern, nsfw=self.nsfw,
max_results=self.options.count): max_results=self.options.count):
self.add_object(video) self.cached_format(video)
self.format(video)
self.flush() self.flush()

View file

@ -133,5 +133,8 @@ class WebContentEdit(ReplApplication):
backend_names = (backend_name,) if backend_name is not None else self.enabled_backends backend_names = (backend_name,) if backend_name is not None else self.enabled_backends
_id = _id.encode('utf-8') _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): for backend, revision in self.do('iter_revisions', _id, max_results=self.options.count, backends=backend_names):
self.format(revision) self.format(revision)
self.flush()

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright(C) 2010-2011 Romain Bignon # Copyright(C) 2010-2012 Romain Bignon
# #
# This file is part of weboob. # This file is part of weboob.
# #
@ -23,7 +23,7 @@ import sys
from weboob.capabilities.torrent import ICapTorrent, MagnetOnly from weboob.capabilities.torrent import ICapTorrent, MagnetOnly
from weboob.tools.application.repl import ReplApplication 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 from weboob.core import CallErrors
@ -40,52 +40,39 @@ def sizeof_fmt(num):
class TorrentInfoFormatter(IFormatter): class TorrentInfoFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'name', 'size', 'seeders', 'leechers', 'url', 'files', 'description') MANDATORY_FIELDS = ('id', 'name', 'size', 'seeders', 'leechers', 'url', 'files', 'description')
def flush(self): def format_obj(self, obj, alias):
pass result = u'%s%s%s\n' % (self.BOLD, obj.name, self.NC)
result += 'ID: %s\n' % obj.fullid
def format_dict(self, item): result += 'Size: %s\n' % sizeof_fmt(obj.size)
result = u'%s%s%s\n' % (self.BOLD, item['name'], self.NC) result += 'Seeders: %s\n' % obj.seeders
result += 'ID: %s\n' % item['id'] result += 'Leechers: %s\n' % obj.leechers
result += 'Size: %s\n' % sizeof_fmt(item['size']) result += 'URL: %s\n' % obj.url
result += 'Seeders: %s\n' % item['seeders'] if hasattr(obj, 'magnet') and obj.magnet:
result += 'Leechers: %s\n' % item['leechers'] result += 'Magnet URL: %s\n' % obj.magnet
result += 'URL: %s\n' % item['url'] if obj.files:
if item['magnet']:
result += 'Magnet URL: %s\n' % item['magnet']
if item['files']:
result += '\n%sFiles%s\n' % (self.BOLD, self.NC) result += '\n%sFiles%s\n' % (self.BOLD, self.NC)
for f in item['files']: for f in obj.files:
result += ' * %s\n' % f result += ' * %s\n' % f
result += '\n%sDescription%s\n' % (self.BOLD, self.NC) result += '\n%sDescription%s\n' % (self.BOLD, self.NC)
result += item['description'] result += obj.description
return result return result
class TorrentListFormatter(IFormatter): class TorrentListFormatter(PrettyFormatter):
MANDATORY_FIELDS = ('id', 'name', 'size', 'seeders', 'leechers') MANDATORY_FIELDS = ('id', 'name', 'size', 'seeders', 'leechers')
count = 0 def get_title(self, obj):
return obj.name
def flush(self): def get_description(self, obj):
self.count = 0 size = sizeof_fmt(obj.size)
pass return '%10s (Seed: %2d / Leech: %2d)' % (size, obj.seeders, obj.leechers)
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
class Weboorrents(ReplApplication): class Weboorrents(ReplApplication):
APPNAME = 'weboorrents' APPNAME = 'weboorrents'
VERSION = '0.c' 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 ' \ DESCRIPTION = 'Console application allowing to search for torrents on various trackers ' \
'and download .torrent files.' 'and download .torrent files.'
CAPS = ICapTorrent CAPS = ICapTorrent
@ -107,19 +94,15 @@ class Weboorrents(ReplApplication):
Get information about a torrent. Get information about a torrent.
""" """
_id, backend_name = self.parse_id(id)
found = 0 torrent = self.get_object(id, 'get_torrent')
for backend, torrent in self.do('get_torrent', _id, backends=backend_name): if not torrent:
if torrent: print >>sys.stderr, 'Torrent not found: %s' % id
self.format(torrent)
found = 1
if not found:
print >>sys.stderr, 'Torrent "%s" not found' % id
return 3 return 3
else:
self.flush() self.start_format()
self.format(torrent)
self.flush()
def complete_getfile(self, text, line, *ignored): def complete_getfile(self, text, line, *ignored):
args = line.split(' ', 2) args = line.split(' ', 2)
@ -178,8 +161,8 @@ class Weboorrents(ReplApplication):
self.change_path([u'search']) self.change_path([u'search'])
if not pattern: if not pattern:
pattern = None 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): for backend, torrent in self.do('iter_torrents', pattern=pattern):
self.add_object(torrent) self.cached_format(torrent)
self.format(torrent)
self.flush() self.flush()

View file

@ -17,62 +17,43 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>. # along with weboob. If not, see <http://www.gnu.org/licenses/>.
from datetime import datetime
from weboob.capabilities.weather import ICapWeather from weboob.capabilities.weather import ICapWeather
from weboob.capabilities.gauge import ICapWaterLevel from weboob.capabilities.gauge import ICapWaterLevel
from weboob.tools.application.repl import ReplApplication 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'] __all__ = ['WetBoobs']
class ForecastsFormatter(IFormatter): class ForecastsFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'date', 'low', 'high', 'unit') MANDATORY_FIELDS = ('id', 'date', 'low', 'high', 'unit')
def flush(self): def format_obj(self, obj, alias):
pass 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:
def format_dict(self, item): result += ' %s' % obj.text
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']
return result return result
class CurrentFormatter(IFormatter): class CurrentFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'date', 'temp') MANDATORY_FIELDS = ('id', 'date', 'temp')
def flush(self): def format_obj(self, obj, alias):
pass result = u'%s%s%s: %s' % (self.BOLD, obj.date, self.NC, obj.temp)
if hasattr(obj, 'unit') and obj.unit:
def format_dict(self, item): result += u'°%s' % obj.unit
if isinstance(item['date'], datetime): if hasattr(obj, 'text') and obj.text:
date = item['date'].strftime('%y-%m-%d %H:%M:%S') result += u' - %s' % obj.text
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']
return result return result
class CitiesFormatter(IFormatter): class CitiesFormatter(PrettyFormatter):
MANDATORY_FIELDS = ('id', 'name') MANDATORY_FIELDS = ('id', 'name')
count = 0
def flush(self): def get_title(self, obj):
self.count = 0 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): class WetBoobs(ReplApplication):
APPNAME = 'wetboobs' APPNAME = 'wetboobs'
@ -97,9 +78,9 @@ class WetBoobs(ReplApplication):
Search cities. Search cities.
""" """
self.change_path(['cities']) self.change_path(['cities'])
self.start_format()
for backend, city in self.do('iter_city_search', pattern, caps=ICapWeather): for backend, city in self.do('iter_city_search', pattern, caps=ICapWeather):
self.add_object(city) self.cached_format(city)
self.format(city)
self.flush() self.flush()
def complete_current(self, text, line, *ignored): def complete_current(self, text, line, *ignored):
@ -115,6 +96,8 @@ class WetBoobs(ReplApplication):
""" """
city, = self.parse_command_args(line, 1, 1) city, = self.parse_command_args(line, 1, 1)
_id, backend_name = self.parse_id(city) _id, backend_name = self.parse_id(city)
self.start_format()
for backend, current in self.do('get_current', _id, backends=backend_name, caps=ICapWeather): for backend, current in self.do('get_current', _id, backends=backend_name, caps=ICapWeather):
if current: if current:
self.format(current) self.format(current)
@ -133,6 +116,8 @@ class WetBoobs(ReplApplication):
""" """
city, = self.parse_command_args(line, 1, 1) city, = self.parse_command_args(line, 1, 1)
_id, backend_name = self.parse_id(city) _id, backend_name = self.parse_id(city)
self.start_format()
for backend, forecast in self.do('iter_forecast', _id, backends=backend_name, caps=ICapWeather): for backend, forecast in self.do('iter_forecast', _id, backends=backend_name, caps=ICapWeather):
self.format(forecast) self.format(forecast)
self.flush() self.flush()
@ -144,9 +129,9 @@ class WetBoobs(ReplApplication):
List all rivers. If PATTERN is specified, search on a pattern. List all rivers. If PATTERN is specified, search on a pattern.
""" """
self.change_path([u'gauges']) self.change_path([u'gauges'])
self.start_format()
for backend, gauge in self.do('iter_gauges', pattern or None, caps=ICapWaterLevel): for backend, gauge in self.do('iter_gauges', pattern or None, caps=ICapWaterLevel):
self.add_object(gauge) self.cached_format(gauge)
self.format(gauge)
self.flush() self.flush()
def complete_gauge(self, text, line, *ignored): def complete_gauge(self, text, line, *ignored):
@ -162,6 +147,8 @@ class WetBoobs(ReplApplication):
""" """
gauge, = self.parse_command_args(line, 1, 1) gauge, = self.parse_command_args(line, 1, 1)
_id, backend_name = self.parse_id(gauge) _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): for backend, measure in self.do('iter_gauge_history', _id, backends=backend_name, caps=ICapWaterLevel):
self.format(measure) self.format(measure)
self.flush() self.flush()
@ -179,6 +166,8 @@ class WetBoobs(ReplApplication):
""" """
gauge, = self.parse_command_args(line, 1, 1) gauge, = self.parse_command_args(line, 1, 1)
_id, backend_name = self.parse_id(gauge) _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): for backend, measure in self.do('get_last_measure', _id, backends=backend_name, caps=ICapWaterLevel):
self.format(measure) self.format(measure)
self.flush() self.flush()

View file

@ -230,7 +230,7 @@ class BaseApplication(object):
return version return version
def _do_complete_obj(self, backend, fields, obj): def _do_complete_obj(self, backend, fields, obj):
if fields: if fields is None or len(fields) > 0:
try: try:
backend.fillobj(obj, fields) backend.fillobj(obj, fields)
except ObjectNotAvailable, e: except ObjectNotAvailable, e:

View file

@ -28,15 +28,18 @@ class CSVFormatter(IFormatter):
def __init__(self, field_separator=u';'): def __init__(self, field_separator=u';'):
IFormatter.__init__(self) IFormatter.__init__(self)
self.field_separator = field_separator self.field_separator = field_separator
self.count = 0 self.started = False
def flush(self): 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'' result = u''
if self.count == 0: if not self.started:
result += self.field_separator.join(item.iterkeys()) + '\n' 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()) result += self.field_separator.join(unicode(v) for v in item.itervalues())
return result return result

View file

@ -47,7 +47,7 @@ else:
finally: finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) 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.ordereddict import OrderedDict
from weboob.tools.application.console import ConsoleApplication from weboob.tools.application.console import ConsoleApplication
@ -78,10 +78,9 @@ class IFormatter(object):
BOLD = property(get_bold) BOLD = property(get_bold)
NC = property(get_nc) 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_keys = display_keys
self.display_header = display_header self.display_header = display_header
self.return_only = return_only
self.interactive = False self.interactive = False
self.print_lines = 0 self.print_lines = 0
self.termrows = 0 self.termrows = 0
@ -94,7 +93,7 @@ class IFormatter(object):
else: else:
self.termrows = int(subprocess.Popen('stty size', shell=True, stdout=subprocess.PIPE).communicate()[0].split()[0]) 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: if self.outfile != sys.stdout:
with open(self.outfile, "a+") as outfile: with open(self.outfile, "a+") as outfile:
outfile.write(formatted.encode('utf-8')) outfile.write(formatted.encode('utf-8'))
@ -113,47 +112,45 @@ class IFormatter(object):
print line print line
self.print_lines += 1 self.print_lines += 1
def build_id(self, v, backend_name): def start_format(self, **kwargs):
return u'%s@%s' % (unicode(v), backend_name) pass
def flush(self): 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. Format an object to be human-readable.
An object has fields which can be selected. 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 obj: object to format
@param selected_fields [tuple] fields to display. If None, all fields are selected :type obj: CapBaseObject
@return a string of the formatted object :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): if selected_fields is not None and not '*' in selected_fields:
item = obj obj = obj.copy()
elif isinstance(obj, tuple): for name, value in obj.iter_fields():
item = OrderedDict([(k, v) for k, v in obj]) if not name in selected_fields:
else: delattr(obj, name)
item = self.to_dict(obj, selected_fields)
if item is None:
return None
if self.MANDATORY_FIELDS: 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: if missing_fields:
raise MandatoryFieldsNotFound(missing_fields) raise MandatoryFieldsNotFound(missing_fields)
formatted = self.format_dict(item=item) formatted = self.format_obj(obj, alias)
if formatted: if formatted:
self.after_format(formatted) self.output(formatted)
return 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 Format a dict to be human-readable. The dict is already simplified
if user provides selected fields. if user provides selected fields.
Called by format(). Called by format().
@ -164,31 +161,37 @@ class IFormatter(object):
""" """
raise NotImplementedError() raise NotImplementedError()
def set_header(self, string): def to_dict(self, obj):
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 iter_decorate(d): def iter_decorate(d):
for key, value in d: for key, value in d:
if key == 'id' and obj.backend is not None: if key == 'id' and obj.backend is not None:
value = self.build_id(value, obj.backend) value = obj.fullid
yield key, value yield key, value
fields_iterator = obj.iter_fields() fields_iterator = obj.iter_fields()
d = OrderedDict(iter_decorate(fields_iterator)) return OrderedDict(iter_decorate(fields_iterator))
return OrderedDict((k, v) for k, v in iter_select(d))
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

View file

@ -34,14 +34,12 @@ class MultilineFormatter(IFormatter):
def flush(self): def flush(self):
pass pass
def format_dict(self, item): def format_obj(self, obj, alias):
item = self.to_dict(obj)
result = u'\n'.join(u'%s%s' % ( result = u'\n'.join(u'%s%s' % (
(u'%s%s' % (k, self.key_value_separator) if self.display_keys else ''), v) (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)) for k, v in item.iteritems() if (v is not NotLoaded and v is not NotAvailable))
if len(item) > 1: if len(item) > 1:
result += self.after_item result += self.after_item
return result return result
def set_header(self, string):
if self.display_header:
print string.encode('utf-8')

View file

@ -30,10 +30,8 @@ class SimpleFormatter(IFormatter):
self.field_separator = field_separator self.field_separator = field_separator
self.key_value_separator = key_value_separator self.key_value_separator = key_value_separator
def flush(self): def format_obj(self, obj, alias):
pass item = self.to_dict(obj)
def format_dict(self, item):
return self.field_separator.join(u'%s%s' % ( return self.field_separator.join(u'%s%s' % (
(u'%s%s' % (k, self.key_value_separator) if self.display_keys else ''), v) (u'%s%s' % (k, self.key_value_separator) if self.display_keys else ''), v)
for k, v in item.iteritems()) for k, v in item.iteritems())

View file

@ -31,18 +31,18 @@ __all__ = ['TableFormatter', 'HTMLTableFormatter']
class TableFormatter(IFormatter): class TableFormatter(IFormatter):
HTML = False HTML = False
def __init__(self, display_keys=True, return_only=False): def __init__(self):
IFormatter.__init__(self, display_keys=display_keys, return_only=return_only) IFormatter.__init__(self)
self.queue = [] self.queue = []
self.keys = None self.keys = None
self.header = 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): 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: if len(self.queue) == 0:
return return
@ -80,14 +80,14 @@ class TableFormatter(IFormatter):
self.queue = [] self.queue = []
if self.return_only: return s
return s
else:
print s.encode('utf-8')
def format_dict(self, item): def format_obj(self, obj, alias):
# format is done in self.flush() by prettytable item = self.to_dict(obj)
return item
if self.keys is None:
self.keys = item.keys()
self.queue.append(item.values())
def set_header(self, string): def set_header(self, string):
self.header = string self.header = string

View file

@ -43,11 +43,8 @@ class WebBrowser(gtk.Window):
class WebkitGtkFormatter(HTMLTableFormatter): class WebkitGtkFormatter(HTMLTableFormatter):
def __init__(self):
HTMLTableFormatter.__init__(self, return_only=True)
def flush(self): def flush(self):
table_string = HTMLTableFormatter.flush(self) table_string = self.get_formatted_table()
js_filepaths = [] js_filepaths = []
js_filepaths.append(get_javascript('jquery')) js_filepaths.append(get_javascript('jquery'))
js_filepaths.append(get_javascript('tablesorter')) js_filepaths.append(get_javascript('tablesorter'))

View file

@ -264,9 +264,9 @@ class ReplApplication(Cmd, ConsoleApplication):
kwargs['backends'] = self.enabled_backends if backends is None else backends kwargs['backends'] = self.enabled_backends if backends is None else backends
kwargs['condition'] = self.condition kwargs['condition'] = self.condition
fields = self.selected_fields fields = self.selected_fields
if fields == '$direct': if '$direct' in fields:
fields = [] fields = []
elif fields == '$full': elif '$full' in fields:
fields = None fields = None
return self.weboob.do(self._do_complete, self.options.count, fields, function, *args, **kwargs) 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: if self.options.select:
self.selected_fields = self.options.select.split(',') self.selected_fields = self.options.select.split(',')
else: else:
self.selected_fields = '$direct' self.selected_fields = ['$direct']
if self.options.condition: if self.options.condition:
self.condition = ResultsCondition(self.options.condition) self.condition = ResultsCondition(self.options.condition)
@ -800,15 +800,9 @@ class ReplApplication(Cmd, ConsoleApplication):
line = line.strip() line = line.strip()
if line: if line:
split = line.split() split = line.split()
if len(split) == 1 and split[0] in ('$direct', '$full'): self.selected_fields = split
self.selected_fields = split[0]
else:
self.selected_fields = split
else: else:
if isinstance(self.selected_fields, basestring): print ' '.join(self.selected_fields)
print self.selected_fields
else:
print ' '.join(self.selected_fields)
def complete_inspect(self, text, line, begidx, endidx): def complete_inspect(self, text, line, begidx, endidx):
return sorted(set(backend.name for backend in self.enabled_backends)) 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 # We have an argument, let's ch to the directory before the ls
self.working_path.cd1(path) 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): if isinstance(obj, CapBaseObject):
self.format(obj) self.cached_format(obj)
else: else:
print obj print obj
@ -1012,14 +1007,24 @@ class ReplApplication(Cmd, ConsoleApplication):
return name return name
def set_formatter_header(self, string): 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 fields = self.selected_fields
if fields in ('$direct', '$full'): if '$direct' in fields or '$full' in fields:
fields = None fields = None
try: try:
self.formatter.format(obj=result, selected_fields=fields) self.formatter.format(obj=result, selected_fields=fields, alias=alias)
except FieldNotFound, e: except FieldNotFound, e:
print >>sys.stderr, e print >>sys.stderr, e
except MandatoryFieldsNotFound, e: except MandatoryFieldsNotFound, e: