support new config management and login callback in applications

This commit is contained in:
Romain Bignon 2011-05-21 10:28:03 +02:00
commit a255916fa3
3 changed files with 207 additions and 121 deletions

View file

@ -18,7 +18,7 @@
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from copy import deepcopy
from copy import copy
import getpass
import logging
import sys
@ -64,8 +64,12 @@ class ConsoleApplication(BaseApplication):
def __init__(self, option_parser=None):
BaseApplication.__init__(self, option_parser)
self.weboob.callbacks['login'] = self.login_cb
self.enabled_backends = set()
def login_cb(self, backend_name, value):
return self.ask('[%s] Password' % backend_name, masked=True, default='', regexp=value.regexp)
def unload_backends(self, *args, **kwargs):
unloaded = self.weboob.unload_backends(*args, **kwargs)
for backend in unloaded.itervalues():
@ -221,8 +225,8 @@ class ConsoleApplication(BaseApplication):
asked_config = True
print 'Configuration of new account %s' % website
print '-----------------------------%s' % ('-' * len(website))
p = deepcopy(prop)
p.set_value(self.ask(prop, default=account.properties[key].value if (key in account.properties) else prop.default))
p = copy(prop)
p.set(self.ask(prop, default=account.properties[key].get() if (key in account.properties) else prop.default))
account.properties[key] = p
if asked_config:
print '-----------------------------%s' % ('-' * len(website))
@ -239,7 +243,7 @@ class ConsoleApplication(BaseApplication):
backend_config = {}
for key, value in account.properties.iteritems():
if key in backend.config:
backend_config[key] = value.value
backend_config[key] = value.get()
if ask_add and self.ask('Do you want to add the new register account?', default=True):
return self.add_backend(name, backend_config, ask_register=False)
@ -253,9 +257,12 @@ class ConsoleApplication(BaseApplication):
if params is None:
params = {}
backend = None
config = None
if not edit:
try:
backend = self.weboob.modules_loader.get_or_load_module(name)
config = backend.config
except ModuleLoadError:
backend = None
else:
@ -264,15 +271,17 @@ class ConsoleApplication(BaseApplication):
backend = self.weboob.modules_loader.get_or_load_module(bname)
except ModuleLoadError:
backend = None
items.update(params)
params = items
else:
items.update(params)
params = items
config = backend.config.load(self.weboob, bname, name, params, nofail=True)
if not backend:
print >>sys.stderr, 'Backend "%s" does not exist. Hint: use the "backends" command.' % name
return 1
# ask for params non-specified on command-line arguments
asked_config = False
for key, value in backend.config.iteritems():
for key, value in config.iteritems():
if not asked_config:
asked_config = True
print 'Configuration of backend'
@ -284,21 +293,23 @@ class ConsoleApplication(BaseApplication):
if asked_config:
print '------------------------'
while not edit and self.weboob.backends_config.backend_exists(name):
print >>sys.stderr, 'Backend instance "%s" already exists in "%s"' % (name, self.weboob.backends_config.confpath)
if not self.ask('Add new instance of "%s" backend?' % backend.name, default=False):
return 1
name = self.ask('Please give new instance name (could be "%s_1")' % backend.name, regexp=r'^[\w\-_]+$')
try:
self.weboob.backends_config.add_backend(name, name, params, edit=edit)
print 'Backend "%s" successfully %s.' % (name, 'updated' if edit else 'added')
config = config.load(self.weboob, backend.name, name, params, nofail=True)
for key, value in params.iteritems():
config[key].set(value)
config.save(edit=edit)
print 'Backend "%s" successfully added.' % name
return name
except BackendAlreadyExists:
print >>sys.stderr, 'Backend "%s" is already configured in file "%s"' % (name, self.weboob.backends_config.confpath)
while self.ask('Add new instance of "%s" backend?' % name, default=False):
new_name = self.ask('Please give new instance name (could be "%s_1")' % name, regexp=r'^[\w\-_]+$')
try:
self.weboob.backends_config.add_backend(new_name, name, params)
print 'Backend "%s" successfully added.' % new_name
return new_name
except BackendAlreadyExists:
print >>sys.stderr, 'Instance "%s" already exists for backend "%s".' % (new_name, name)
return 1
print >>sys.stderr, 'Instance "%s" already exists.' % name
return 1
def ask(self, question, default=None, masked=False, regexp=None, choices=None):
"""
@ -313,7 +324,7 @@ class ConsoleApplication(BaseApplication):
"""
if isinstance(question, Value):
v = deepcopy(question)
v = copy(question)
if default:
v.default = default
if masked:
@ -388,13 +399,13 @@ class ConsoleApplication(BaseApplication):
line = aliases[line]
try:
v.set_value(line)
v.set(line)
except ValueError, e:
print >>sys.stderr, 'Error: %s' % e
else:
break
return v.value
return v.get()
def bcall_error_handler(self, backend, error, backtrace):
"""

View file

@ -62,13 +62,14 @@ class BackendCfg(QDialog):
self.ui.configuredBackendsList.header().setResizeMode(QHeaderView.ResizeToContents)
self.ui.configFrame.hide()
self.icon_cache = {}
for name, backend in self.weboob.modules_loader.loaded.iteritems():
if not self.caps or backend.has_caps(*self.caps):
item = QListWidgetItem(name.capitalize())
if backend.icon_path:
img = QImage(backend.icon_path)
item.setIcon(QIcon(QPixmap.fromImage(img)))
item.setIcon(self.get_icon_cache(backend.icon_path))
self.ui.backendsList.addItem(item)
@ -86,6 +87,12 @@ class BackendCfg(QDialog):
self.connect(self.ui.configButtonBox, SIGNAL('accepted()'), self.acceptBackend)
self.connect(self.ui.configButtonBox, SIGNAL('rejected()'), self.rejectBackend)
def get_icon_cache(self, path):
if not path in self.icon_cache:
img = QImage(path)
self.icon_cache[path] = QIcon(QPixmap.fromImage(img))
return self.icon_cache[path]
def loadConfiguredBackendsList(self):
self.ui.configuredBackendsList.clear()
for instance_name, name, params in self.weboob.backends_config.iter_backends():
@ -102,8 +109,7 @@ class BackendCfg(QDialog):
else Qt.Unchecked)
if backend.icon_path:
img = QImage(backend.icon_path)
item.setIcon(0, QIcon(QPixmap.fromImage(img)))
item.setIcon(0, self.get_icon_cache(backend.icon_path))
self.ui.configuredBackendsList.addTopLevelItem(item)
@ -159,22 +165,23 @@ class BackendCfg(QDialog):
self.ui.configFrame.hide()
self.loadConfiguredBackendsList()
def editBackend(self, bname=None):
def editBackend(self, name=None):
self.ui.registerButton.hide()
self.ui.configFrame.show()
if bname is not None:
mname, params = self.weboob.backends_config.get_backend(bname)
if name is not None:
bname, params = self.weboob.backends_config.get_backend(name)
items = self.ui.backendsList.findItems(mname, Qt.MatchFixedString)
items = self.ui.backendsList.findItems(bname, Qt.MatchFixedString)
if not items:
warning('Backend not found')
else:
self.ui.backendsList.setCurrentItem(items[0])
self.ui.backendsList.setEnabled(False)
self.ui.nameEdit.setText(bname)
self.ui.nameEdit.setText(name)
self.ui.nameEdit.setEnabled(False)
if '_proxy' in params:
self.ui.proxyBox.setChecked(True)
self.ui.proxyEdit.setText(params.pop('_proxy'))
@ -184,13 +191,14 @@ class BackendCfg(QDialog):
params.pop('_enabled', None)
for key, value in params.iteritems():
backend = self.weboob.modules_loader.loaded[bname]
for key, value in backend.config.load(self.weboob, bname, name, params, nofail=True).iteritems():
try:
l, widget = self.config_widgets[key]
except KeyError:
warning('Key "%s" is not found' % key)
else:
widget.set_data(value)
widget.set_value(value)
else:
self.ui.nameEdit.clear()
self.ui.nameEdit.setEnabled(True)
@ -199,69 +207,6 @@ class BackendCfg(QDialog):
self.ui.backendsList.setEnabled(True)
self.ui.backendsList.setCurrentRow(-1)
def acceptBackend(self):
bname = unicode(self.ui.nameEdit.text())
selection = self.ui.backendsList.selectedItems()
if not selection:
QMessageBox.critical(self, self.tr('Unable to add a configured backend'),
self.tr('Please select a backend'))
return
try:
backend = self.weboob.modules_loader.get_or_load_module(unicode(selection[0].text()).lower())
except ModuleLoadError:
backend = None
if not backend:
QMessageBox.critical(self, self.tr('Unable to add a configured backend'),
self.tr('The selected backend does not exist.'))
return
params = {}
if not bname:
QMessageBox.critical(self, self.tr('Missing field'), self.tr('Please specify a backend name'))
return
if self.ui.nameEdit.isEnabled() and not re.match(r'^[\w\-_]+$', bname):
QMessageBox.critical(self, self.tr('Invalid value'),
self.tr('The backend name can only contain letters and digits'))
return
if self.ui.proxyBox.isChecked():
params['_proxy'] = unicode(self.ui.proxyEdit.text())
if not params['_proxy']:
QMessageBox.critical(self, self.tr('Missing field'), self.tr('Please specify a proxy URL'))
return
for key, field in backend.config.iteritems():
label, qtvalue = self.config_widgets[key]
try:
value = qtvalue.get_value()
except ValueError, e:
QMessageBox.critical(self, self.tr('Invalid value'),
unicode(self.tr('Invalid value for field "%s":<br /><br />%s')) % (field.label, e))
return
params[key] = value.value
try:
self.weboob.backends_config.add_backend(bname, backend.name, params, edit=not self.ui.nameEdit.isEnabled())
except BackendAlreadyExists, e:
QMessageBox.critical(self, self.tr('Unable to create backend'),
unicode(self.tr('Unable to create backend "%s": it already exists')) % bname)
return
self.to_load.add(bname)
self.ui.configFrame.hide()
self.loadConfiguredBackendsList()
def rejectBackend(self):
self.ui.configFrame.hide()
def backendSelectionChanged(self):
for key, (label, value) in self.config_widgets.iteritems():
label.hide()
@ -288,6 +233,7 @@ class BackendCfg(QDialog):
self.ui.nameEdit.setText(backend.name)
else:
self.ui.nameEdit.setText('')
self.ui.backendInfo.setText(unicode(self.tr(
'<h1>%s Backend %s</h1>'
'<b>Version</b>: %s<br />'
@ -313,13 +259,82 @@ class BackendCfg(QDialog):
for key, field in backend.config.iteritems():
label = QLabel(u'%s:' % field.label)
value = QtValue(field)
self.ui.configLayout.addRow(label, value)
self.config_widgets[key] = (label, value)
qvalue = QtValue(field)
self.ui.configLayout.addRow(label, qvalue)
self.config_widgets[key] = (label, qvalue)
def proxyEditEnabled(self, state):
self.ui.proxyEdit.setEnabled(state)
def acceptBackend(self):
bname = unicode(self.ui.nameEdit.text())
selection = self.ui.backendsList.selectedItems()
if not selection:
QMessageBox.critical(self, self.tr('Unable to add a configured backend'),
self.tr('Please select a backend'))
return
try:
backend = self.weboob.modules_loader.get_or_load_module(unicode(selection[0].text()).lower())
except ModuleLoadError:
backend = None
if not backend:
QMessageBox.critical(self, self.tr('Unable to add a configured backend'),
self.tr('The selected backend does not exist.'))
return
params = {}
if not bname:
QMessageBox.critical(self, self.tr('Missing field'), self.tr('Please specify a backend name'))
return
if self.ui.nameEdit.isEnabled():
if not re.match(r'^[\w\-_]+$', bname):
QMessageBox.critical(self, self.tr('Invalid value'),
self.tr('The backend name can only contain letters and digits'))
return
if self.weboob.backends_config.backend_exists(bname):
QMessageBox.critical(self, self.tr('Unable to create backend'),
unicode(self.tr('Unable to create backend "%s": it already exists')) % bname)
return
if self.ui.proxyBox.isChecked():
params['_proxy'] = unicode(self.ui.proxyEdit.text())
if not params['_proxy']:
QMessageBox.critical(self, self.tr('Missing field'), self.tr('Please specify a proxy URL'))
return
config = backend.config.load(self.weboob, backend.name, bname, {}, nofail=True)
for key, field in config.iteritems():
label, qtvalue = self.config_widgets[key]
try:
value = qtvalue.get_value()
except ValueError, e:
QMessageBox.critical(self, self.tr('Invalid value'),
unicode(self.tr('Invalid value for field "%s":<br /><br />%s')) % (field.label, e))
return
field.set(value.get())
try:
config.save(edit=not self.ui.nameEdit.isEnabled(), params=params)
except BackendAlreadyExists:
QMessageBox.critical(self, self.tr('Unable to create backend'),
unicode(self.tr('Unable to create backend "%s": it already exists')) % bname)
return
self.to_load.add(bname)
self.ui.configFrame.hide()
self.loadConfiguredBackendsList()
def rejectBackend(self):
self.ui.configFrame.hide()
def registerEvent(self):
selection = self.ui.backendsList.selectedItems()
if not selection:
@ -380,7 +395,7 @@ class BackendCfg(QDialog):
else:
for key, value in account.properties.iteritems():
if key in self.config_widgets:
self.config_widgets[key][1].set_data(value.value)
self.config_widgets[key][1].set_value(value)
def run(self):
self.exec_()

View file

@ -20,16 +20,18 @@
import sys
import logging
import re
from copy import deepcopy
from PyQt4.QtCore import QTimer, SIGNAL, QObject, QString, QSize, QVariant
from threading import Event
from copy import copy
from PyQt4.QtCore import QTimer, SIGNAL, QObject, QString, QSize, QVariant, QMutex
from PyQt4.QtGui import QMainWindow, QApplication, QStyledItemDelegate, \
QStyleOptionViewItemV4, QTextDocument, QStyle, \
QAbstractTextDocumentLayout, QPalette, QMessageBox, \
QSpinBox, QLineEdit, QComboBox, QCheckBox
QSpinBox, QLineEdit, QComboBox, QCheckBox, QInputDialog, \
QLineEdit
from weboob.core.ouiboube import Weboob
from weboob.core.scheduler import IScheduler
from weboob.tools.value import ValueInt, ValueBool
from weboob.tools.value import ValueInt, ValueBool, ValueBackendPassword
from ..base import BaseApplication
@ -77,12 +79,63 @@ class QtScheduler(IScheduler):
def run(self):
self.app.exec_()
class QCallbacksManager(QObject):
class Request(object):
def __init__(self):
self.event = Event()
self.answer = None
def __call__(self):
raise NotImplementedError()
class LoginRequest(Request):
def __init__(self, backend_name, value):
QCallbacksManager.Request.__init__(self)
self.backend_name = backend_name
self.value = value
def __call__(self):
password, ok = QInputDialog.getText(None,
'Password request',
'Please enter password for %s' % self.backend_name,
QLineEdit.Password)
return password
def __init__(self, weboob, parent=None):
QObject.__init__(self, parent)
self.weboob = weboob
self.weboob.callbacks['login'] = self.callback(self.LoginRequest)
self.mutex = QMutex()
self.requests = []
self.connect(self, SIGNAL('new_request'), self.do_request)
def callback(self, klass):
def cb(*args, **kwargs):
return self.add_request(klass(*args, **kwargs))
return cb
def do_request(self):
self.mutex.lock()
request = self.requests.pop()
request.answer = request()
request.event.set()
self.mutex.unlock()
def add_request(self, request):
self.mutex.lock()
self.requests.append(request)
self.mutex.unlock()
self.emit(SIGNAL('new_request'))
request.event.wait()
return request.answer
class QtApplication(QApplication, BaseApplication):
def __init__(self):
QApplication.__init__(self, sys.argv)
self.setApplicationName(self.APPNAME)
BaseApplication.__init__(self)
self.cbmanager = QCallbacksManager(self.weboob, self)
def create_weboob(self):
return Weboob(scheduler=QtScheduler(self))
@ -196,14 +249,19 @@ class _QtValueStr(QLineEdit):
if value.masked:
self.setEchoMode(self.Password)
def set_data(self, text):
self._value.set_value(unicode(text))
self.setText(self._value.value)
def set_value(self, value):
self._value = value
self.setText(self._value.get())
def get_value(self):
self._value.set_value(unicode(self.text()))
self._value.set(unicode(self.text()))
return self._value
class _QtValueBackendPassword(_QtValueStr):
def get_value(self):
self._value._domain = None
return _QtValueStr.get_value(self)
class _QtValueBool(QCheckBox):
def __init__(self, value):
QCheckBox.__init__(self)
@ -211,12 +269,12 @@ class _QtValueBool(QCheckBox):
if value.default:
self.setChecked(True)
def set_data(self, b):
self._value.set_value(b)
self.setChecked(self._value.value)
def set_value(self, value):
self._value = value
self.setChecked(self._value.get())
def get_value(self):
self._value.set_value(self.isChecked())
self._value.set(self.isChecked())
return self._value
class _QtValueInt(QSpinBox):
@ -226,12 +284,12 @@ class _QtValueInt(QSpinBox):
if value.default:
self.setValue(int(value.default))
def set_data(self, i):
self._value.set_value(i)
self.setValue(self._value.value)
def set_value(self, value):
self._value = value
self.setValue(self._value.get())
def get_value(self):
self._value.set_value(self.getValue())
self._value.set(self.getValue())
return self._value
class _QtValueChoices(QComboBox):
@ -243,15 +301,15 @@ class _QtValueChoices(QComboBox):
if value.default == k:
self.setCurrentIndex(self.count()-1)
def set_data(self, c):
self._value.set_value(c)
def set_value(self, value):
self._value = value
for i in xrange(self.count()):
if unicode(self.itemData(i).toString()) == self._value.value:
if unicode(self.itemData(i).toString()) == self._value.get():
self.setCurrentIndex(i)
return
def get_value(self):
self._value.set_value(unicode(self.itemData(self.currentIndex()).toString()))
self._value.set(unicode(self.itemData(self.currentIndex()).toString()))
return self._value
def QtValue(value):
@ -259,9 +317,11 @@ def QtValue(value):
klass = _QtValueBool
elif isinstance(value, ValueInt):
klass = _QtValueInt
elif isinstance(value, ValueBackendPassword):
klass = _QtValueBackendPassword
elif value.choices is not None:
klass = _QtValueChoices
else:
klass = _QtValueStr
return klass(deepcopy(value))
return klass(copy(value))