From e8e7fbead17e19fd61b358077433a7b2ca223db1 Mon Sep 17 00:00:00 2001 From: Romain Bignon Date: Mon, 26 Mar 2012 23:05:23 +0200 Subject: [PATCH] add application comparoob --- man/comparoob.1 | 211 +++++++++++++++++++++ scripts/comparoob | 9 + weboob/applications/comparoob/__init__.py | 7 + weboob/applications/comparoob/comparoob.py | 135 +++++++++++++ 4 files changed, 362 insertions(+) create mode 100644 man/comparoob.1 create mode 100755 scripts/comparoob create mode 100644 weboob/applications/comparoob/__init__.py create mode 100644 weboob/applications/comparoob/comparoob.py diff --git a/man/comparoob.1 b/man/comparoob.1 new file mode 100644 index 0000000000..7a60b377c6 --- /dev/null +++ b/man/comparoob.1 @@ -0,0 +1,211 @@ +.TH COMPAROOB 1 "26 March 2012" "comparoob 0\&.c" +.SH NAME +comparoob +.SH SYNOPSIS +.B comparoob +[\-dqv] [\-b \fIbackends\fR] [\-cnfs] [\fIcommand\fR [\fIarguments\fR..]] +.br +.B comparoob +[\-\-help] [\-\-version] + +.SH DESCRIPTION +.LP + +Console application to compare products. + +.SS Supported websites: +* prixcarburants (French governement website to compare fuel prices) +.SH COMPAROOB COMMANDS +.TP +\fBinfo\fR +.TP +\fBprices\fR +.SH WEBOOB COMMANDS +.TP +\fBbackends\fR [\fIACTION\fR] [\fIBACKEND_NAME\fR]... +.br +Select used backends. +.br + +.br +ACTION is one of the following (default: list): +.br +* enable enable given backends +.br +* disable disable given backends +.br +* only enable given backends and disable the others +.br +* list display enabled and available backends +.br +* add add a backend +.br +* register register a new account on a website +.br +* edit edit a backend +.br +* remove remove a backend +.TP +\fBcd\fR [\fIPATH\fR] +.br +Follow a path. +.br +".." is a special case and goes up one directory. +.br +"" is a special case and goes home. +.TP +\fBcondition\fR [\fIEXPRESSION\fR | off] +.br +If an argument is given, set the condition expression used to filter the results. +.br +If the "off" value is given, conditional filtering is disabled. +.br + +.br +If no argument is given, print the current condition expression. +.TP +\fBcount\fR [\fINUMBER\fR | off] +.br +If an argument is given, set the maximum number of results fetched. +.br +NUMBER must be at least 1. +.br +"off" value disables counting, and allows infinite searches. +.br + +.br +If no argument is given, print the current count value. +.TP +\fBformatter\fR [list | \fIFORMATTER\fR [\fICOMMAND\fR] | option \fIOPTION_NAME\fR [on | off]] +.br +If a FORMATTER is given, set the formatter to use. +.br +You can add a COMMAND to apply the formatter change only to +.br +a given command. +.br + +.br +If the argument is "list", print the available formatters. +.br + +.br +If the argument is "option", set the formatter options. +.br +Valid options are: header, keys. +.br +If on/off value is given, set the value of the option. +.br +If not, print the current value for the option. +.br + +.br +If no argument is given, print the current formatter. +.TP +\fBinspect\fR \fIBACKEND_NAME\fR +.br +Display the HTML string of the current page of the specified backend's browser. +.br + +.br +If webkit_mechanize_browser Python module is installed, HTML is displayed in a WebKit GUI. +.TP +\fBlogging\fR [\fILEVEL\fR] +.br +Set logging level. +.br + +.br +Availables: debug, info, warning, error. +.br +* quiet is an alias for error +.br +* default is an alias for warning +.TP +\fBls\fR [\fIPATH\fR] +.br +List objects in current path. +.br +If an argument is given, list the specified path. +.TP +\fBquit\fR +.br +Quit the application. +.TP +\fBselect\fR [\fIFIELD_NAME\fR]... | "$direct" | "$full" +.br +If an argument is given, set the selected fields. +.br +$direct selects all fields loaded in one http request. +.br +$full selects all fields using as much http requests as necessary. +.br + +.br +If no argument is given, print the currently selected fields. + +.SH OPTIONS +.TP +\fB\-\-version\fR +show program's version number and exit +.TP +\fB\-h\fR, \fB\-\-help\fR +show this help message and exit +.TP +\fB\-b BACKENDS\fR, \fB\-\-backends=BACKENDS\fR +what backend(s) to enable (comma separated) + +.SH LOGGING OPTIONS +.TP +\fB\-d\fR, \fB\-\-debug\fR +display debug messages +.TP +\fB\-q\fR, \fB\-\-quiet\fR +display only error messages +.TP +\fB\-v\fR, \fB\-\-verbose\fR +display info messages +.TP +\fB\-\-logging\-file=LOGGING_FILE\fR +file to save logs +.TP +\fB\-a\fR, \fB\-\-save\-responses\fR +save every response + +.SH RESULTS OPTIONS +.TP +\fB\-c CONDITION\fR, \fB\-\-condition=CONDITION\fR +filter result items to display given a boolean expression +.TP +\fB\-n COUNT\fR, \fB\-\-count=COUNT\fR +get a maximum number of results (all backends merged) +.TP +\fB\-s SELECT\fR, \fB\-\-select=SELECT\fR +select result item keys to display (comma separated) + +.SH FORMATTING OPTIONS +.TP +\fB\-f FORMATTER\fR, \fB\-\-formatter=FORMATTER\fR +select output formatter (csv, htmltable, multiline, price, prices, simple, +table, webkit) +.TP +\fB\-\-no\-header\fR +do not display header +.TP +\fB\-\-no\-keys\fR +do not display item keys +.TP +\fB\-O OUTFILE\fR, \fB\-\-outfile=OUTFILE\fR +file to export result + +.SH COPYRIGHT +Copyright(C) 2012 Romain Bignon +.LP +For full COPYRIGHT see COPYING file with weboob package. +.LP +.RE +.SH FILES +"~/.config/weboob/backends" + +.SH SEE ALSO +Home page: http://weboob.org/applications/comparoob diff --git a/scripts/comparoob b/scripts/comparoob new file mode 100755 index 0000000000..2fa3e379e2 --- /dev/null +++ b/scripts/comparoob @@ -0,0 +1,9 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# vim: ft=python et softtabstop=4 cinoptions=4 shiftwidth=4 ts=4 ai + +from weboob.applications.comparoob import Comparoob + + +if __name__ == '__main__': + Comparoob.run() diff --git a/weboob/applications/comparoob/__init__.py b/weboob/applications/comparoob/__init__.py new file mode 100644 index 0000000000..394b92e6ca --- /dev/null +++ b/weboob/applications/comparoob/__init__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +# vim: ft=python et softtabstop=4 cinoptions=4 shiftwidth=4 ts=4 ai + + +from .comparoob import Comparoob + +__all__ = ['Comparoob'] diff --git a/weboob/applications/comparoob/comparoob.py b/weboob/applications/comparoob/comparoob.py new file mode 100644 index 0000000000..1bd5205a5c --- /dev/null +++ b/weboob/applications/comparoob/comparoob.py @@ -0,0 +1,135 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2012 Romain Bignon +# +# This file is part of weboob. +# +# weboob is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# weboob is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with weboob. If not, see . + + +from __future__ import with_statement + +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 + + +__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'] + else: + message = '%s (%s)' % (item['shop'].name, item['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 += '\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() + + return result + + +class PricesFormatter(IFormatter): + MANDATORY_FIELDS = ('id', 'cost', 'currency', 'shop') + + count = 0 + + def flush(self): + self.count = 0 + + def format_dict(self, item): + self.count += 1 + if item['message']: + message = item['message'] + else: + message = '%s (%s)' % (item['shop'].name, item['shop'].location) + + 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' + VERSION = '0.c' + COPYRIGHT = 'Copyright(C) 2012 Romain Bignon' + DESCRIPTION = 'Console application to compare products.' + DEFAULT_FORMATTER = 'table' + EXTRA_FORMATTERS = {'prices': PricesFormatter, + 'price': PriceFormatter, + } + COMMANDS_FORMATTERS = {'prices': 'prices', + 'info': 'price', + } + CAPS = ICapPriceComparison + + def do_prices(self, pattern): + products = [] + for backend, product in self.do('search_products', pattern): + products.append(product) + + if len(products) == 0: + print >>sys.stderr, 'Error: no product found with this pattern' + return 1 + elif len(products) == 1: + product = products[0] + else: + print >>sys.stderr, 'Error: too many results, TODO' + return 1 + + self.change_path([u'prices']) + for backend, price in self.do('iter_prices', product): + self.add_object(price) + self.format(price) + + def complete_info(self, text, line, *ignored): + args = line.split(' ') + if len(args) == 2: + return self._complete_object() + + def do_info(self, _id): + if not _id: + print >>sys.stderr, 'This command takes an argument: %s' % self.get_command_help('info', short=True) + return 2 + + price = self.get_object(_id, 'get_price') + if not price: + print >>sys.stderr, 'Price not found: %s' % _id + return 3 + self.format(price) + self.flush() -- GitLab