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 = '' - 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: