PEP8 style fixes and other small style fixes

I used autopep8 on some files and did carefully check the changes.
I ignored E501,E302,E231,E225,E222,E221,E241,E203 in my search, and at
least E501 on any autopep8 run.

Other style fixes not related to PEP8:
* Only use new-style classes. I don't think the usage of old-style
  classes was voluntary. Old-style classes are removed in Python 3.
* Convert an if/else to a one-liner in mediawiki, change docstring style
  change to a comment something that wasn't really appropriate for a
  docstring.
* Unneeded first if condition in meteofrance
This commit is contained in:
Laurent Bachelier 2012-03-14 03:24:02 +01:00
commit 006e97a8be
99 changed files with 441 additions and 350 deletions

View file

@ -298,7 +298,7 @@ class AuMBackend(BaseBackend, ICapMessages, ICapMessagesPost, ICapDating, ICapCh
yield thread.root yield thread.root
except BrowserUnavailable, e: except BrowserUnavailable, e:
self.logger.debug('No messages, browser is unavailable: %s' % e) self.logger.debug('No messages, browser is unavailable: %s' % e)
pass # don't care about waiting pass # don't care about waiting
def set_message_read(self, message): def set_message_read(self, message):
if message.id == self.MAGIC_ID_BASKET: if message.id == self.MAGIC_ID_BASKET:

View file

@ -24,7 +24,7 @@ import Image
class CaptchaError(Exception): pass class CaptchaError(Exception): pass
class Tile: class Tile(object):
hash = { hash = {
'bc8d52d96058478a6def26226145d53b': 'A', 'bc8d52d96058478a6def26226145d53b': 'A',
'c62ecdfddb72b2feaed96cd9fe7c2802': 'A', 'c62ecdfddb72b2feaed96cd9fe7c2802': 'A',
@ -111,7 +111,7 @@ class Tile:
print 'hash: %s' % checksum print 'hash: %s' % checksum
raise CaptchaError() raise CaptchaError()
class Captcha: class Captcha(object):
def __init__(self, f): def __init__(self, f):
self.img = Image.open(f) self.img = Image.open(f)
self.w, self.h = self.img.size self.w, self.h = self.img.size
@ -152,7 +152,7 @@ class Captcha:
s += tile.letter s += tile.letter
return s return s
class Decoder: class Decoder(object):
def __init__(self): def __init__(self):
self.hash = {} self.hash = {}

View file

@ -26,7 +26,8 @@ from weboob.tools.ordereddict import OrderedDict
from weboob.capabilities.contact import Contact as _Contact, ProfileNode from weboob.capabilities.contact import Contact as _Contact, ProfileNode
from weboob.tools.misc import html2text from weboob.tools.misc import html2text
class FieldBase:
class FieldBase(object):
def __init__(self, key, key2=None): def __init__(self, key, key2=None):
self.key = key self.key = key
self.key2 = key2 self.key2 = key2
@ -34,18 +35,22 @@ class FieldBase:
def get_value(self, value, consts): def get_value(self, value, consts):
raise NotImplementedError() raise NotImplementedError()
class FieldStr(FieldBase): class FieldStr(FieldBase):
def get_value(self, profile, consts): def get_value(self, profile, consts):
return html2text(unicode(profile[self.key])).strip() return html2text(unicode(profile[self.key])).strip()
class FieldBool(FieldBase): class FieldBool(FieldBase):
def get_value(self, profile, consts): def get_value(self, profile, consts):
return bool(int(profile[self.key])) return bool(int(profile[self.key]))
class FieldDist(FieldBase): class FieldDist(FieldBase):
def get_value(self, profile, consts): def get_value(self, profile, consts):
return '%.2f km' % float(profile[self.key]) return '%.2f km' % float(profile[self.key])
class FieldIP(FieldBase): class FieldIP(FieldBase):
def get_hostname(self, s): def get_hostname(self, s):
try: try:
@ -59,6 +64,7 @@ class FieldIP(FieldBase):
s += ' (first %s)' % self.get_hostname(profile[self.key2]) s += ' (first %s)' % self.get_hostname(profile[self.key2])
return s return s
class FieldProfileURL(FieldBase): class FieldProfileURL(FieldBase):
def get_value(self, profile, consts): def get_value(self, profile, consts):
id = int(profile[self.key]) id = int(profile[self.key])
@ -67,10 +73,12 @@ class FieldProfileURL(FieldBase):
else: else:
return '' return ''
class FieldPopu(FieldBase): class FieldPopu(FieldBase):
def get_value(self, profile, consts): def get_value(self, profile, consts):
return unicode(profile['popu'][self.key]) return unicode(profile['popu'][self.key])
class FieldPopuRatio(FieldBase): class FieldPopuRatio(FieldBase):
def get_value(self, profile, consts): def get_value(self, profile, consts):
v1 = float(profile['popu'][self.key]) v1 = float(profile['popu'][self.key])
@ -80,15 +88,18 @@ class FieldPopuRatio(FieldBase):
else: else:
return '%.2f' % (v1 / v2) return '%.2f' % (v1 / v2)
class FieldOld(FieldBase): class FieldOld(FieldBase):
def get_value(self, profile, consts): def get_value(self, profile, consts):
birthday = parse_dt(profile[self.key]) birthday = parse_dt(profile[self.key])
return int((datetime.now() - birthday).days / 365.25) return int((datetime.now() - birthday).days / 365.25)
class FieldSplit(FieldBase): class FieldSplit(FieldBase):
def get_value(self, profile, consts): def get_value(self, profile, consts):
return [html2text(s).strip() for s in profile[self.key].split(self.key2) if len(s.strip()) > 0] return [html2text(s).strip() for s in profile[self.key].split(self.key2) if len(s.strip()) > 0]
class FieldBMI(FieldBase): class FieldBMI(FieldBase):
def __init__(self, key, key2, fat=False): def __init__(self, key, key2, fat=False):
FieldBase.__init__(self, key, key2) FieldBase.__init__(self, key, key2)
@ -100,7 +111,7 @@ class FieldBMI(FieldBase):
if height == 0 or weight == 0: if height == 0 or weight == 0:
return '' return ''
bmi = (weight/float(pow(height/100.0, 2))) bmi = (weight / float(pow(height / 100.0, 2)))
if not self.fat: if not self.fat:
return bmi return bmi
elif bmi < 15.5: elif bmi < 15.5:
@ -114,6 +125,7 @@ class FieldBMI(FieldBase):
else: else:
return 'obese' return 'obese'
class FieldFlags(FieldBase): class FieldFlags(FieldBase):
def get_value(self, profile, consts): def get_value(self, profile, consts):
i = int(profile[self.key]) i = int(profile[self.key])
@ -123,6 +135,7 @@ class FieldFlags(FieldBase):
labels.append(html2text(d['label']).strip()) labels.append(html2text(d['label']).strip())
return labels return labels
class FieldList(FieldBase): class FieldList(FieldBase):
def get_value(self, profile, consts): def get_value(self, profile, consts):
i = int(profile[self.key]) i = int(profile[self.key])
@ -131,6 +144,7 @@ class FieldList(FieldBase):
return html2text(d['label']).strip() return html2text(d['label']).strip()
return '' return ''
class Contact(_Contact): class Contact(_Contact):
TABLE = OrderedDict(( TABLE = OrderedDict((
('_info', OrderedDict(( ('_info', OrderedDict((
@ -247,9 +261,9 @@ class Contact(_Contact):
if node.flags & node.SECTION: if node.flags & node.SECTION:
result += u'\t' * level + node.label + '\n' result += u'\t' * level + node.label + '\n'
for sub in node.value.itervalues(): for sub in node.value.itervalues():
result += print_node(sub, level+1) result += print_node(sub, level + 1)
else: else:
if isinstance(node.value, (tuple,list)): if isinstance(node.value, (tuple, list)):
value = ', '.join(unicode(v) for v in node.value) value = ', '.join(unicode(v) for v in node.value)
elif isinstance(node.value, float): elif isinstance(node.value, float):
value = '%.2f' % node.value value = '%.2f' % node.value

View file

@ -124,9 +124,9 @@ class PriorityConnection(Optimization):
browser = AuMBrowser('%s@%s' % (name, self.config['domain']), proxy=self.browser.proxy) browser = AuMBrowser('%s@%s' % (name, self.config['domain']), proxy=self.browser.proxy)
try: try:
browser.register(password= password, browser.register(password= password,
sex= 1, #slut sex= 1, # slut
birthday_d= random.randint(1,28), birthday_d= random.randint(1, 28),
birthday_m= random.randint(1,12), birthday_m= random.randint(1, 12),
birthday_y= random.randint(1975, 1990), birthday_y= random.randint(1975, 1990),
zipcode= 75001, zipcode= 75001,
country= 'fr', country= 'fr',

View file

@ -49,7 +49,7 @@ class ProfilesWalker(Optimization):
def start(self): def start(self):
self.walk_cron = self.sched.repeat(60, self.enqueue_profiles) self.walk_cron = self.sched.repeat(60, self.enqueue_profiles)
self.view_cron = self.sched.schedule(randint(10,40), self.view_profile) self.view_cron = self.sched.schedule(randint(10, 40), self.view_profile)
return True return True
def stop(self): def stop(self):
@ -77,7 +77,7 @@ class ProfilesWalker(Optimization):
try: try:
id = self.profiles_queue.pop() id = self.profiles_queue.pop()
except KeyError: except KeyError:
return # empty queue return # empty queue
try: try:
with self.browser: with self.browser:
@ -101,4 +101,4 @@ class ProfilesWalker(Optimization):
print e print e
finally: finally:
if self.view_cron is not None: if self.view_cron is not None:
self.view_cron = self.sched.schedule(randint(10,40), self.view_profile) self.view_cron = self.sched.schedule(randint(10, 40), self.view_profile)

View file

@ -22,7 +22,6 @@ from weboob.tools.test import BackendTest
from weboob.tools.browser import BrowserUnavailable from weboob.tools.browser import BrowserUnavailable
__all__ = ['AuMTest'] __all__ = ['AuMTest']

View file

@ -21,6 +21,7 @@ from weboob.tools.capabilities.gallery.genericcomicreader import GenericComicRea
__all__ = ['BatotoBackend'] __all__ = ['BatotoBackend']
class BatotoBackend(GenericComicReaderBackend): class BatotoBackend(GenericComicReaderBackend):
NAME = 'batoto' NAME = 'batoto'
DESCRIPTION = 'Batoto manga reading website' DESCRIPTION = 'Batoto manga reading website'
@ -31,4 +32,4 @@ class BatotoBackend(GenericComicReaderBackend):
ID_REGEXP = r'[^/]+/[^/]+' ID_REGEXP = r'[^/]+/[^/]+'
URL_REGEXP = r'.+batoto.(?:com|net)/read/_/(%s).+' % ID_REGEXP URL_REGEXP = r'.+batoto.(?:com|net)/read/_/(%s).+' % ID_REGEXP
ID_TO_URL = 'http://www.batoto.net/read/_/%s' ID_TO_URL = 'http://www.batoto.net/read/_/%s'
PAGES = { URL_REGEXP: DisplayPage } PAGES = {URL_REGEXP: DisplayPage}

View file

@ -19,8 +19,9 @@
from weboob.tools.capabilities.gallery.genericcomicreader import GenericComicReaderTest from weboob.tools.capabilities.gallery.genericcomicreader import GenericComicReaderTest
class BatotoTest(GenericComicReaderTest): class BatotoTest(GenericComicReaderTest):
BACKEND = 'batoto' BACKEND = 'batoto'
def test_download(self): def test_download(self):
return self._test_download('26287/yurumates_ch4_by_primitive-scans') return self._test_download('26287/yurumates_ch4_by_primitive-scans')

View file

@ -70,4 +70,3 @@ class BoursoramaBackend(BaseBackend, ICapBank):
with self.browser: with self.browser:
for coming in self.browser.get_coming_operations(account): for coming in self.browser.get_coming_operations(account):
yield coming yield coming

View file

@ -32,7 +32,7 @@ __all__ = ['Boursorama']
class Boursorama(BaseBrowser): class Boursorama(BaseBrowser):
DOMAIN = 'www.boursorama.com' DOMAIN = 'www.boursorama.com'
PROTOCOL = 'https' PROTOCOL = 'https'
ENCODING = None # refer to the HTML encoding ENCODING = None # refer to the HTML encoding
PAGES = { PAGES = {
'.*connexion.phtml.*': LoginPage, '.*connexion.phtml.*': LoginPage,
'.*/comptes/synthese.phtml': AccountsList, '.*/comptes/synthese.phtml': AccountsList,
@ -84,11 +84,11 @@ class Boursorama(BaseBrowser):
self.location(account._link_id) self.location(account._link_id)
operations = self.page.get_operations() operations = self.page.get_operations()
# load last month as well # load last month as well
target = date.today() - relativedelta( months = 1 ) target = date.today() - relativedelta(months=1)
self.location(account._link_id + ("&month=%d&year=%d" % (target.month, target.year))) self.location(account._link_id + ("&month=%d&year=%d" % (target.month, target.year)))
operations += self.page.get_operations() operations += self.page.get_operations()
# and the month before, just in case you're greedy # and the month before, just in case you're greedy
target = date.today() - relativedelta( months = 2 ) target = date.today() - relativedelta(months=2)
self.location(account._link_id + ("&month=%d&year=%d" % (target.month, target.year))) self.location(account._link_id + ("&month=%d&year=%d" % (target.month, target.year)))
operations += self.page.get_operations() operations += self.page.get_operations()
for index, op in enumerate(operations): for index, op in enumerate(operations):

View file

@ -23,7 +23,8 @@ from .account_history import AccountHistory
from .accounts_list import AccountsList from .accounts_list import AccountsList
from .login import LoginPage from .login import LoginPage
class AccountPrelevement(AccountsList): pass class AccountPrelevement(AccountsList):
pass
__all__ = ['LoginPage', __all__ = ['LoginPage',
'AccountsList', 'AccountsList',

View file

@ -22,8 +22,8 @@
from weboob.capabilities.bank import Account from weboob.capabilities.bank import Account
from weboob.tools.browser import BasePage from weboob.tools.browser import BasePage
class AccountsList(BasePage):
class AccountsList(BasePage):
def on_loaded(self): def on_loaded(self):
pass pass
@ -34,34 +34,34 @@ class AccountsList(BasePage):
for tr in div.getiterator('tr'): for tr in div.getiterator('tr'):
account = Account() account = Account()
for td in tr.getiterator('td'): for td in tr.getiterator('td'):
if td.attrib.get('class', '') == 'account-cb': if td.attrib.get('class', '') == 'account-cb':
break break
elif td.attrib.get('class', '') == 'account-name': elif td.attrib.get('class', '') == 'account-name':
a = td.find('a') a = td.find('a')
account.label = a.text account.label = a.text
account._link_id = a.get('href', '') account._link_id = a.get('href', '')
elif td.attrib.get('class', '') == 'account-number': elif td.attrib.get('class', '') == 'account-number':
id = td.text id = td.text
id = id.strip(u' \n\t') id = id.strip(u' \n\t')
account.id = id account.id = id
elif td.attrib.get('class', '') == 'account-total': elif td.attrib.get('class', '') == 'account-total':
span = td.find('span') span = td.find('span')
if span == None: if span == None:
balance = td.text balance = td.text
else: else:
balance = span.text balance = span.text
balance = balance.strip(u' \n\t€+').replace(',','.').replace(' ','') balance = balance.strip(u' \n\t€+').replace(',', '.').replace(' ', '')
if balance != "": if balance != "":
account.balance = float(balance) account.balance = float(balance)
else: else:
account.balance = 0.0 account.balance = 0.0
else: else:
# because of some weird useless <tr> # because of some weird useless <tr>
if account.id != 0: if account.id != 0:
l.append(account) l.append(account)
return l return l

View file

@ -26,7 +26,7 @@ __all__ = ['LoginPage']
class LoginPage(BasePage): class LoginPage(BasePage):
def on_loaded(self): def on_loaded(self):
pass pass
# for td in self.document.getroot().cssselect('td.LibelleErreur'): # for td in self.document.getroot().cssselect('td.LibelleErreur'):
# if td.text is None: # if td.text is None:
# continue # continue

View file

@ -35,7 +35,7 @@ __all__ = ['BPBrowser']
class BPBrowser(BaseBrowser): class BPBrowser(BaseBrowser):
DOMAIN = 'voscomptesenligne.labanquepostale.fr' DOMAIN = 'voscomptesenligne.labanquepostale.fr'
PROTOCOL = 'https' PROTOCOL = 'https'
ENCODING = None # refer to the HTML encoding ENCODING = None # refer to the HTML encoding
PAGES = {r'.*wsost/OstBrokerWeb/loginform.*' : LoginPage, PAGES = {r'.*wsost/OstBrokerWeb/loginform.*' : LoginPage,
r'.*authentification/repositionnerCheminCourant-identif.ea' : repositionnerCheminCourant, r'.*authentification/repositionnerCheminCourant-identif.ea' : repositionnerCheminCourant,
r'.*authentification/initialiser-identif.ea' : Initident, r'.*authentification/initialiser-identif.ea' : Initident,

View file

@ -54,7 +54,7 @@ class AccountHistory(BasePage):
for t in tmp: for t in tmp:
if r.search(t.text): if r.search(t.text):
amount = t.text amount = t.text
amount = ''.join( amount.replace('.', '').replace(',', '.').split() ) amount = ''.join(amount.replace('.', '').replace(',', '.').split())
if amount[0] == "-": if amount[0] == "-":
operation.amount = -float(amount[1:]) operation.amount = -float(amount[1:])
else: else:

View file

@ -26,6 +26,7 @@ __all__ = ['CanalplusVideo']
class CanalplusVideo(BaseVideo): class CanalplusVideo(BaseVideo):
swf_player = False swf_player = False
@classmethod @classmethod
def id2url(cls, _id): def id2url(cls, _id):
return 'http://service.canal-plus.com/video/rest/getVideosLiees/cplus/%s' % _id return 'http://service.canal-plus.com/video/rest/getVideosLiees/cplus/%s' % _id

View file

@ -33,8 +33,10 @@ from lxml import etree
from datetime import date from datetime import date
from StringIO import StringIO from StringIO import StringIO
__all__ = ['CmbBackend'] __all__ = ['CmbBackend']
class CmbBackend(BaseBackend, ICapBank): class CmbBackend(BaseBackend, ICapBank):
NAME = 'cmb' NAME = 'cmb'
MAINTAINER = 'Johann Broudin' MAINTAINER = 'Johann Broudin'
@ -83,7 +85,6 @@ class CmbBackend(BaseBackend, ICapBank):
) )
] ]
cookie = None cookie = None
headers = { headers = {
'User-Agent': 'User-Agent':
@ -167,13 +168,13 @@ class CmbBackend(BaseBackend, ICapBank):
balance = td[1].text balance = td[1].text
balance = balance.replace(',','.').replace(u"\xa0",'') balance = balance.replace(',', '.').replace(u"\xa0", '')
account.balance = float(balance) account.balance = float(balance)
span = td[3].xpath('a/span') span = td[3].xpath('a/span')
if len(span): if len(span):
coming = span[0].text.replace(' ','').replace(',','.') coming = span[0].text.replace(' ', '').replace(',', '.')
coming = coming.replace(u"\xa0",'') coming = coming.replace(u"\xa0", '')
account.coming = float(coming) account.coming = float(coming)
else: else:
account.coming = NotAvailable account.coming = NotAvailable
@ -248,7 +249,7 @@ class CmbBackend(BaseBackend, ICapBank):
operation.date = date(*reversed([int(x) for x in d])) operation.date = date(*reversed([int(x) for x in d]))
div = td[2].xpath('div') div = td[2].xpath('div')
label = div[0].xpath('a')[0].text.replace('\n','') label = div[0].xpath('a')[0].text.replace('\n', '')
operation.raw = unicode(' '.join(label.split())) operation.raw = unicode(' '.join(label.split()))
for pattern, _type, _label in self.LABEL_PATTERNS: for pattern, _type, _label in self.LABEL_PATTERNS:
mm = pattern.match(operation.raw) mm = pattern.match(operation.raw)
@ -260,12 +261,11 @@ class CmbBackend(BaseBackend, ICapBank):
amount = td[3].text amount = td[3].text
if amount.count(',') != 1: if amount.count(',') != 1:
amount = td[4].text amount = td[4].text
amount = amount.replace(',','.').replace(u'\xa0','') amount = amount.replace(',', '.').replace(u'\xa0', '')
operation.amount = float(amount) operation.amount = float(amount)
else: else:
amount = amount.replace(',','.').replace(u'\xa0','') amount = amount.replace(',', '.').replace(u'\xa0', '')
operation.amount = - float(amount) operation.amount = - float(amount)
i += 1 i += 1
yield operation yield operation

View file

@ -24,6 +24,7 @@ from weboob.capabilities.bank import Account
from .base import CragrBasePage from .base import CragrBasePage
from weboob.capabilities.bank import Transaction from weboob.capabilities.bank import Transaction
def clean_amount(amount): def clean_amount(amount):
""" """
Removes weird characters and converts to a float Removes weird characters and converts to a float
@ -34,6 +35,7 @@ def clean_amount(amount):
matches = re.findall('^(-?[0-9]+\.[0-9]{2}).*$', data) matches = re.findall('^(-?[0-9]+\.[0-9]{2}).*$', data)
return float(matches[0]) if (matches) else 0.0 return float(matches[0]) if (matches) else 0.0
class AccountsList(CragrBasePage): class AccountsList(CragrBasePage):
def get_list(self): def get_list(self):
@ -109,7 +111,7 @@ class AccountsList(CragrBasePage):
select_name is the name of the select field to analyze select_name is the name of the select field to analyze
""" """
if not self.is_transfer_page(): if not self.is_transfer_page():
return False return False
source_accounts = {} source_accounts = {}
source_account_options = self.document.xpath('/html/body//form//select[@name="%s"]/option' % select_name) source_account_options = self.document.xpath('/html/body//form//select[@name="%s"]/option' % select_name)
for option in source_account_options: for option in source_account_options:
@ -212,7 +214,7 @@ class AccountsList(CragrBasePage):
year = today.year year = today.year
return date(year, month, day) return date(year, month, day)
def get_history(self, start_index = 0, start_offset = 0): def get_history(self, start_index=0, start_offset=0):
""" """
Returns the history of a specific account. Note that this function Returns the history of a specific account. Note that this function
expects the current page to be the one dedicated to this history. expects the current page to be the one dedicated to this history.

View file

@ -26,6 +26,7 @@ from .pages import LoginPage, LoginErrorPage, AccountsPage, UserSpacePage, Opera
__all__ = ['CreditMutuelBrowser'] __all__ = ['CreditMutuelBrowser']
# Browser # Browser
class CreditMutuelBrowser(BaseBrowser): class CreditMutuelBrowser(BaseBrowser):
PROTOCOL = 'https' PROTOCOL = 'https'
@ -36,11 +37,11 @@ class CreditMutuelBrowser(BaseBrowser):
'https://www.creditmutuel.fr/groupe/fr/identification/default.cgi': LoginErrorPage, 'https://www.creditmutuel.fr/groupe/fr/identification/default.cgi': LoginErrorPage,
'https://www.creditmutuel.fr/.*/fr/banque/situation_financiere.cgi': AccountsPage, 'https://www.creditmutuel.fr/.*/fr/banque/situation_financiere.cgi': AccountsPage,
'https://www.creditmutuel.fr/.*/fr/banque/espace_personnel.aspx': UserSpacePage, 'https://www.creditmutuel.fr/.*/fr/banque/espace_personnel.aspx': UserSpacePage,
'https://www.creditmutuel.fr/.*/fr/banque/mouvements.cgi.*' : OperationsPage, 'https://www.creditmutuel.fr/.*/fr/banque/mouvements.cgi.*': OperationsPage,
'https://www.creditmutuel.fr/.*/fr/banque/nr/nr_devbooster.aspx.*' : OperationsPage, 'https://www.creditmutuel.fr/.*/fr/banque/nr/nr_devbooster.aspx.*': OperationsPage,
'https://www.creditmutuel.fr/.*/fr/banque/operations_carte\.cgi.*' : OperationsPage, 'https://www.creditmutuel.fr/.*/fr/banque/operations_carte\.cgi.*': OperationsPage,
'https://www.creditmutuel.fr/.*/fr/banque/BAD.*' : InfoPage, 'https://www.creditmutuel.fr/.*/fr/banque/BAD.*': InfoPage,
'https://www.creditmutuel.fr/.*/fr/banque/.*Vir.*' : TransfertPage 'https://www.creditmutuel.fr/.*/fr/banque/.*Vir.*': TransfertPage
} }
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -61,17 +62,17 @@ class CreditMutuelBrowser(BaseBrowser):
if not self.is_on_page(LoginPage): if not self.is_on_page(LoginPage):
self.location('https://www.creditmutuel.fr/', no_login=True) self.location('https://www.creditmutuel.fr/', no_login=True)
self.page.login( self.username, self.password) self.page.login(self.username, self.password)
if not self.is_logged() or self.is_on_page(LoginErrorPage): if not self.is_logged() or self.is_on_page(LoginErrorPage):
raise BrowserIncorrectPassword() raise BrowserIncorrectPassword()
self.SUB_BANKS = ['cmdv','cmcee','cmse', 'cmidf', 'cmsmb', 'cmma', 'cmmabn', 'cmc', 'cmlaco', 'cmnormandie', 'cmm'] self.SUB_BANKS = ['cmdv', 'cmcee', 'cmse', 'cmidf', 'cmsmb', 'cmma', 'cmmabn', 'cmc', 'cmlaco', 'cmnormandie', 'cmm']
self.getCurrentSubBank() self.getCurrentSubBank()
def get_accounts_list(self): def get_accounts_list(self):
if not self.is_on_page(AccountsPage): if not self.is_on_page(AccountsPage):
self.location('https://www.creditmutuel.fr/%s/fr/banque/situation_financiere.cgi'%self.currentSubBank) self.location('https://www.creditmutuel.fr/%s/fr/banque/situation_financiere.cgi' % self.currentSubBank)
return self.page.get_list() return self.page.get_list()
def get_account(self, id): def get_account(self, id):
@ -121,13 +122,13 @@ class CreditMutuelBrowser(BaseBrowser):
def transfer(self, account, to, amount, reason=None): def transfer(self, account, to, amount, reason=None):
# access the transfer page # access the transfer page
transfert_url = 'WI_VPLV_VirUniSaiCpt.asp?RAZ=ALL&Cat=6&PERM=N&CHX=A' transfert_url = 'WI_VPLV_VirUniSaiCpt.asp?RAZ=ALL&Cat=6&PERM=N&CHX=A'
self.location('https://%s/%s/fr/banque/%s'%(self.DOMAIN, self.currentSubBank, transfert_url)) self.location('https://%s/%s/fr/banque/%s' % (self.DOMAIN, self.currentSubBank, transfert_url))
# fill the form # fill the form
self.select_form(name='FormVirUniSaiCpt') self.select_form(name='FormVirUniSaiCpt')
self['IDB'] = [account[-1]] self['IDB'] = [account[-1]]
self['ICR'] = [to[-1]] self['ICR'] = [to[-1]]
self['MTTVIR'] = '%s' % str(amount).replace('.',',') self['MTTVIR'] = '%s' % str(amount).replace('.', ',')
if reason != None: if reason != None:
self['LIBDBT'] = reason self['LIBDBT'] = reason
self['LIBCRT'] = reason self['LIBCRT'] = reason

View file

@ -27,7 +27,7 @@ from ..tools import url2id
from .index import DLFPPage from .index import DLFPPage
class RSSComment(DLFPPage): class RSSComment(DLFPPage):
def on_loaded(self): def on_loaded(self):
pass pass
class Content(object): class Content(object):

View file

@ -21,6 +21,7 @@ from weboob.tools.capabilities.gallery.genericcomicreader import GenericComicRea
__all__ = ['EatmangaBackend'] __all__ = ['EatmangaBackend']
class EatmangaBackend(GenericComicReaderBackend): class EatmangaBackend(GenericComicReaderBackend):
NAME = 'eatmanga' NAME = 'eatmanga'
DESCRIPTION = 'EatManga manga reading website' DESCRIPTION = 'EatManga manga reading website'
@ -31,4 +32,4 @@ class EatmangaBackend(GenericComicReaderBackend):
ID_REGEXP = r'[^/]+/[^/]+' ID_REGEXP = r'[^/]+/[^/]+'
URL_REGEXP = r'.+eatmanga.com/(?:index.php/)?Manga-Scan/(%s).+' % ID_REGEXP URL_REGEXP = r'.+eatmanga.com/(?:index.php/)?Manga-Scan/(%s).+' % ID_REGEXP
ID_TO_URL = 'http://www.eatmanga.com/index.php/Manga-Scan/%s' ID_TO_URL = 'http://www.eatmanga.com/index.php/Manga-Scan/%s'
PAGES = { URL_REGEXP: DisplayPage } PAGES = {URL_REGEXP: DisplayPage}

View file

@ -19,8 +19,9 @@
from weboob.tools.capabilities.gallery.genericcomicreader import GenericComicReaderTest from weboob.tools.capabilities.gallery.genericcomicreader import GenericComicReaderTest
class EatmangaTest(GenericComicReaderTest): class EatmangaTest(GenericComicReaderTest):
BACKEND = 'eatmanga' BACKEND = 'eatmanga'
def test_download(self): def test_download(self):
return self._test_download('Glass-Mask/Glass-Mask-Vol-031') return self._test_download('Glass-Mask/Glass-Mask-Vol-031')

View file

@ -96,4 +96,4 @@ class EHentaiBackend(BaseBackend, ICapGallery):
OBJECTS = { OBJECTS = {
EHentaiGallery: fill_gallery, EHentaiGallery: fill_gallery,
EHentaiImage: fill_image } EHentaiImage: fill_image}

View file

@ -61,7 +61,7 @@ class EHentaiBrowser(BaseBrowser):
assert self.is_on_page(GalleryPage) assert self.is_on_page(GalleryPage)
i = 0 i = 0
while True: while True:
n = self.page._next_page_link(); n = self.page._next_page_link()
for img in self.page.image_pages(): for img in self.page.image_pages():
yield EHentaiImage(img) yield EHentaiImage(img)
@ -104,4 +104,3 @@ class EHentaiBrowser(BaseBrowser):
# necessary in order to reach the fjords # necessary in order to reach the fjords
self.home() self.home()

View file

@ -27,6 +27,7 @@ from .gallery import EHentaiGallery
__all__ = ['GalleryPage', 'ImagePage', 'IndexPage', 'HomePage', 'LoginPage'] __all__ = ['GalleryPage', 'ImagePage', 'IndexPage', 'HomePage', 'LoginPage']
class LoginPage(BasePage): class LoginPage(BasePage):
def is_logged(self): def is_logged(self):
success_p = self.document.xpath( success_p = self.document.xpath(
@ -38,9 +39,11 @@ class LoginPage(BasePage):
print 'not logged on' print 'not logged on'
return False return False
class HomePage(BasePage): class HomePage(BasePage):
pass pass
class IndexPage(BasePage): class IndexPage(BasePage):
def iter_galleries(self): def iter_galleries(self):
lines = self.document.xpath('//table[@class="itg"]//tr[@class="gtr0" or @class="gtr1"]') lines = self.document.xpath('//table[@class="itg"]//tr[@class="gtr0" or @class="gtr1"]')
@ -50,6 +53,7 @@ class IndexPage(BasePage):
title = a.text.strip() title = a.text.strip()
yield EHentaiGallery(re.search('(?<=/g/)\d+/[\dabcdef]+', url).group(0), title=title) yield EHentaiGallery(re.search('(?<=/g/)\d+/[\dabcdef]+', url).group(0), title=title)
class GalleryPage(BasePage): class GalleryPage(BasePage):
def image_pages(self): def image_pages(self):
return self.document.xpath('//div[@class="gdtm"]//a/attribute::href') return self.document.xpath('//div[@class="gdtm"]//a/attribute::href')
@ -102,7 +106,7 @@ class GalleryPage(BasePage):
except IndexError: except IndexError:
return None return None
class ImagePage(BasePage): class ImagePage(BasePage):
def get_url(self): def get_url(self):
return self.document.xpath('//div[@class="sni"]/a/img/attribute::src')[0] return self.document.xpath('//div[@class="sni"]/a/img/attribute::src')[0]

View file

@ -20,6 +20,7 @@
from weboob.tools.test import BackendTest from weboob.tools.test import BackendTest
class EHentaiTest(BackendTest): class EHentaiTest(BackendTest):
BACKEND = 'ehentai' BACKEND = 'ehentai'
@ -35,4 +36,3 @@ class EHentaiTest(BackendTest):
self.backend.fillobj(img, ('url',)) self.backend.fillobj(img, ('url',))
self.assertTrue(v.url and v.url.startswith('http://'), 'URL for first image in gallery "%s" not found: %s' % (v.id, img.url)) self.assertTrue(v.url and v.url.startswith('http://'), 'URL for first image in gallery "%s" not found: %s' % (v.id, img.url))
self.backend.browser.openurl(img.url) self.backend.browser.openurl(img.url)

View file

@ -68,7 +68,7 @@ class FourChanBackend(BaseBackend, ICapMessages):
thread = Thread(id) thread = Thread(id)
thread.title = _thread.filename thread.title = _thread.filename
thread.root = Message(thread=thread, thread.root = Message(thread=thread,
id=0, # root message id=0, # root message
title=_thread.filename, title=_thread.filename,
sender=_thread.author, sender=_thread.author,
receivers=None, receivers=None,

View file

@ -30,9 +30,11 @@ __all__ = ['ValidationPage', 'HomePage', 'HistoryPage', 'StoryPage']
class ValidationPage(BasePage): class ValidationPage(BasePage):
pass pass
class HomePage(BasePage): class HomePage(BasePage):
pass pass
class Author(object): class Author(object):
(UNKNOWN, (UNKNOWN,
MALE, MALE,
@ -45,6 +47,7 @@ class Author(object):
self.email = None self.email = None
self.description = None self.description = None
class Story(object): class Story(object):
def __init__(self, id): def __init__(self, id):
self.id = id self.id = id
@ -54,6 +57,7 @@ class Story(object):
self.author = None self.author = None
self.body = None self.body = None
class HistoryPage(BasePage): class HistoryPage(BasePage):
def get_numerous(self): def get_numerous(self):
td = self.parser.select(self.document.getroot(), 'td.t0', 1) td = self.parser.select(self.document.getroot(), 'td.t0', 1)
@ -89,6 +93,7 @@ class HistoryPage(BasePage):
yield story yield story
story = None story = None
class StoryPage(BasePage): class StoryPage(BasePage):
def get_story(self): def get_story(self):
p_tags = self.document.getroot().xpath('//body/p') p_tags = self.document.getroot().xpath('//body/p')
@ -179,4 +184,3 @@ class AuthorPage(BasePage):
if author.description.startswith(u'0 récit '): if author.description.startswith(u'0 récit '):
self.logger.warning('This author does not have published any story.') self.logger.warning('This author does not have published any story.')
return author return author

View file

@ -18,22 +18,24 @@
# 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/>.
import re import re
def id2url(_id): def id2url(_id):
"return an url from an id" "return an url from an id"
regexp2 = re.compile("(\w+).([0-9]+).(.*$)") regexp2 = re.compile("(\w+).([0-9]+).(.*$)")
match = regexp2.match(_id) match = regexp2.match(_id)
if match: if match:
return 'http://www.20minutes.fr/%s/%s/%s' % ( match.group(1), return 'http://www.20minutes.fr/%s/%s/%s' % (match.group(1),
match.group(2), match.group(2),
match.group(3)) match.group(3))
else: else:
raise ValueError("id doesn't match") raise ValueError("id doesn't match")
def url2id(url): def url2id(url):
"return an id from an url" "return an id from an url"
return url return url
def rssid(entry): def rssid(entry):
return url2id(entry.id) return url2id(entry.id)

View file

@ -32,6 +32,7 @@ from weboob.tools.browser import BaseBrowser, BasePage
__all__ = ['IzneoBackend'] __all__ = ['IzneoBackend']
class ReaderV2(BasePage): class ReaderV2(BasePage):
def get_ean(self): def get_ean(self):
return self.document.xpath("//div[@id='viewer']/attribute::rel")[0] return self.document.xpath("//div[@id='viewer']/attribute::rel")[0]
@ -43,14 +44,15 @@ class ReaderV2(BasePage):
% ean)) % ean))
for page in pages: for page in pages:
width = 1200 # maximum width width = 1200 # maximum width
yield BaseImage(page['page'], yield BaseImage(page['page'],
gallery=gallery, gallery=gallery,
url=("http://www.izneo.com/playerv2/%s/%s/%s/%d/%s" % url=("http://www.izneo.com/playerv2/%s/%s/%s/%d/%s" %
(page['expires'], page['token'], ean, width, page['page']))) (page['expires'], page['token'], ean, width, page['page'])))
class IzneoBrowser(BaseBrowser): class IzneoBrowser(BaseBrowser):
PAGES = { r'http://.+\.izneo.\w+/readv2-.+': ReaderV2 } PAGES = {r'http://.+\.izneo.\w+/readv2-.+': ReaderV2}
def iter_gallery_images(self, gallery): def iter_gallery_images(self, gallery):
self.location(gallery.url) self.location(gallery.url)
@ -88,7 +90,7 @@ class IzneoBackend(BaseBackend, ICapGallery):
return gallery return gallery
def fill_gallery(self, gallery, fields): def fill_gallery(self, gallery, fields):
gallery.title = gallery.id gallery.title = gallery.id
def fill_image(self, image, fields): def fill_image(self, image, fields):
with self.browser: with self.browser:
@ -96,4 +98,4 @@ class IzneoBackend(BaseBackend, ICapGallery):
OBJECTS = { OBJECTS = {
BaseGallery: fill_gallery, BaseGallery: fill_gallery,
BaseImage: fill_image } BaseImage: fill_image}

View file

@ -67,4 +67,3 @@ class LCLBackend(BaseBackend, ICapBank):
with self.browser: with self.browser:
for history in self.browser.get_history(account): for history in self.browser.get_history(account):
yield history yield history

View file

@ -28,17 +28,18 @@ import tempfile
import math import math
import random import random
class LCLVirtKeyboard(MappedVirtKeyboard): class LCLVirtKeyboard(MappedVirtKeyboard):
symbols={'0':'9da2724133f2221482013151735f033c', symbols={'0': '9da2724133f2221482013151735f033c',
'1':'873ab0087447610841ae1332221be37b', '1': '873ab0087447610841ae1332221be37b',
'2':'93ce6c330393ff5980949d7b6c800f77', '2': '93ce6c330393ff5980949d7b6c800f77',
'3':'b2d70c69693784e1bf1f0973d81223c0', '3': 'b2d70c69693784e1bf1f0973d81223c0',
'4':'498c8f5d885611938f94f1c746c32978', '4': '498c8f5d885611938f94f1c746c32978',
'5':'359bcd60a9b8565917a7bf34522052c3', '5': '359bcd60a9b8565917a7bf34522052c3',
'6':'aba912172f21f78cd6da437cfc4cdbd0', '6': 'aba912172f21f78cd6da437cfc4cdbd0',
'7':'f710190d6b947869879ec02d8e851dfa', '7': 'f710190d6b947869879ec02d8e851dfa',
'8':'b42cc25e1539a15f767aa7a641f3bfec', '8': 'b42cc25e1539a15f767aa7a641f3bfec',
'9':'cc60e5894a9d8e12ee0c2c104c1d5490' '9': 'cc60e5894a9d8e12ee0c2c104c1d5490'
} }
url="/outil/UAUT/Clavier/creationClavier?random=" url="/outil/UAUT/Clavier/creationClavier?random="
@ -66,9 +67,11 @@ class LCLVirtKeyboard(MappedVirtKeyboard):
code+=self.get_symbol_code(self.symbols[c]) code+=self.get_symbol_code(self.symbols[c])
return code return code
class SkipPage(BasePage): class SkipPage(BasePage):
pass pass
class LoginPage(BasePage): class LoginPage(BasePage):
def myXOR(self,value,seed): def myXOR(self,value,seed):
s='' s=''
@ -120,6 +123,7 @@ class LoginPage(BasePage):
return True return True
return False return False
class AccountsPage(BasePage): class AccountsPage(BasePage):
def get_list(self): def get_list(self):
l = [] l = []
@ -145,6 +149,7 @@ class AccountsPage(BasePage):
l.append(account) l.append(account)
return l return l
class AccountHistoryPage(BasePage): class AccountHistoryPage(BasePage):
def get_operations(self,account): def get_operations(self,account):
operations = [] operations = []
@ -194,5 +199,3 @@ class AccountHistoryPage(BasePage):
operation.amount=amount operation.amount=amount
operations.append(operation) operations.append(operation)
return operations return operations

View file

@ -20,6 +20,7 @@
from weboob.tools.capabilities.messages.genericArticle import GenericNewsPage, remove_from_selector_list, drop_comments, try_drop_tree, try_remove_from_selector_list from weboob.tools.capabilities.messages.genericArticle import GenericNewsPage, remove_from_selector_list, drop_comments, try_drop_tree, try_remove_from_selector_list
class ArticlePage(GenericNewsPage): class ArticlePage(GenericNewsPage):
"ArticlePage object for inrocks" "ArticlePage object for inrocks"
def on_loaded(self): def on_loaded(self):
@ -51,7 +52,6 @@ class ArticlePage(GenericNewsPage):
a.drop_tree() a.drop_tree()
div.drop_tree() div.drop_tree()
# This part of the article seems manually generated. # This part of the article seems manually generated.
for crappy_title in self.parser.select(element_body, 'p strong'): for crappy_title in self.parser.select(element_body, 'p strong'):
if crappy_title.text == 'LIRE AUSSI :' or crappy_title.text == 'LIRE AUSSI:': if crappy_title.text == 'LIRE AUSSI :' or crappy_title.text == 'LIRE AUSSI:':

View file

@ -20,11 +20,12 @@
from weboob.tools.capabilities.messages.genericArticle import GenericNewsPage from weboob.tools.capabilities.messages.genericArticle import GenericNewsPage
class FlashActuPage(GenericNewsPage): class FlashActuPage(GenericNewsPage):
"ArticlePage object for inrocks" "ArticlePage object for inrocks"
def on_loaded(self): def on_loaded(self):
self.main_div = self.document.getroot() self.main_div = self.document.getroot()
self.element_title_selector = "h1" self.element_title_selector = "h1"
self.element_author_selector = "div.name>span" self.element_author_selector = "div.name>span"
self.element_body_selector = "h2" self.element_body_selector = "h2"
@ -32,4 +33,3 @@ class FlashActuPage(GenericNewsPage):
element_body = self.get_element_body() element_body = self.get_element_body()
element_body.tag = "div" element_body.tag = "div"
return self.parser.tostring(element_body) return self.parser.tostring(element_body)

View file

@ -20,10 +20,11 @@
from weboob.tools.capabilities.messages.genericArticle import GenericNewsPage from weboob.tools.capabilities.messages.genericArticle import GenericNewsPage
class SimplePage(GenericNewsPage): class SimplePage(GenericNewsPage):
"ArticlePage object for minutes20" "ArticlePage object for minutes20"
def on_loaded(self): def on_loaded(self):
self.main_div = self.document.getroot() self.main_div = self.document.getroot()
self.element_author_selector = "div.mna-signature" self.element_author_selector = "div.mna-signature"
self.element_body_selector = "#article" self.element_body_selector = "#article"

View file

@ -19,11 +19,12 @@
from weboob.tools.capabilities.messages.genericArticle import GenericNewsPage, try_remove_from_selector_list from weboob.tools.capabilities.messages.genericArticle import GenericNewsPage, try_remove_from_selector_list
class SpecialPage(GenericNewsPage): class SpecialPage(GenericNewsPage):
"ArticlePage object for inrocks" "ArticlePage object for inrocks"
def on_loaded(self): def on_loaded(self):
self.main_div = self.document.getroot() self.main_div = self.document.getroot()
self.element_title_selector = "h2" self.element_title_selector = "h2"
self.element_author_selector = "div.name>span" self.element_author_selector = "div.name>span"
self.element_body_selector = ".block-text" self.element_body_selector = ".block-text"
@ -32,4 +33,3 @@ class SpecialPage(GenericNewsPage):
try_remove_from_selector_list(self.parser, element_body, ['div']) try_remove_from_selector_list(self.parser, element_body, ['div'])
element_body.tag = "div" element_body.tag = "div"
return self.parser.tostring(element_body) return self.parser.tostring(element_body)

View file

@ -18,22 +18,24 @@
# 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/>.
import re import re
def id2url(_id): def id2url(_id):
"return an url from an id" "return an url from an id"
regexp2 = re.compile("(\w+).([0-9]+).(.*$)") regexp2 = re.compile("(\w+).([0-9]+).(.*$)")
match = regexp2.match(_id) match = regexp2.match(_id)
if match: if match:
return 'http://www.20minutes.fr/%s/%s/%s' % ( match.group(1), return 'http://www.20minutes.fr/%s/%s/%s' % (match.group(1),
match.group(2), match.group(2),
match.group(3)) match.group(3))
else: else:
raise ValueError("id doesn't match") raise ValueError("id doesn't match")
def url2id(url): def url2id(url):
"return an id from an url" "return an id from an url"
return url return url
def rssid(entry): def rssid(entry):
return url2id(entry.id) return url2id(entry.id)

View file

@ -21,6 +21,7 @@ from weboob.tools.capabilities.gallery.genericcomicreader import GenericComicRea
__all__ = ['MangafoxBackend'] __all__ = ['MangafoxBackend']
class MangafoxBackend(GenericComicReaderBackend): class MangafoxBackend(GenericComicReaderBackend):
NAME = 'mangafox' NAME = 'mangafox'
DESCRIPTION = 'Manga Fox manga reading website' DESCRIPTION = 'Manga Fox manga reading website'
@ -31,4 +32,4 @@ class MangafoxBackend(GenericComicReaderBackend):
ID_REGEXP = r'[^/]+/[^/]+(?:/[^/]+)?' ID_REGEXP = r'[^/]+/[^/]+(?:/[^/]+)?'
URL_REGEXP = r'.+mangafox.com/manga/(%s).*' % ID_REGEXP URL_REGEXP = r'.+mangafox.com/manga/(%s).*' % ID_REGEXP
ID_TO_URL = 'http://www.mangafox.com/manga/%s' ID_TO_URL = 'http://www.mangafox.com/manga/%s'
PAGES = { r'http://.+\.mangafox.\w+/manga/[^/]+/[^/]+/([^/]+/)?(.+\.html)?': DisplayPage } PAGES = {r'http://.+\.mangafox.\w+/manga/[^/]+/[^/]+/([^/]+/)?(.+\.html)?': DisplayPage}

View file

@ -20,7 +20,9 @@
from weboob.tools.capabilities.gallery.genericcomicreader import GenericComicReaderTest from weboob.tools.capabilities.gallery.genericcomicreader import GenericComicReaderTest
class MangafoxTest(GenericComicReaderTest): class MangafoxTest(GenericComicReaderTest):
BACKEND = 'mangafox' BACKEND = 'mangafox'
def test_download(self): def test_download(self):
return self._test_download('glass_no_kamen/v02/c000') return self._test_download('glass_no_kamen/v02/c000')

View file

@ -21,6 +21,7 @@ from weboob.tools.capabilities.gallery.genericcomicreader import GenericComicRea
__all__ = ['MangahereBackend'] __all__ = ['MangahereBackend']
class MangahereBackend(GenericComicReaderBackend): class MangahereBackend(GenericComicReaderBackend):
NAME = 'mangahere' NAME = 'mangahere'
DESCRIPTION = 'Manga Here manga reading website' DESCRIPTION = 'Manga Here manga reading website'
@ -31,4 +32,4 @@ class MangahereBackend(GenericComicReaderBackend):
ID_REGEXP = r'[^/]+/[^/]+/[^/]+' ID_REGEXP = r'[^/]+/[^/]+/[^/]+'
URL_REGEXP = r'.+mangahere.com/manga/(%s).+' % ID_REGEXP URL_REGEXP = r'.+mangahere.com/manga/(%s).+' % ID_REGEXP
ID_TO_URL = 'http://www.mangahere.com/manga/%s' ID_TO_URL = 'http://www.mangahere.com/manga/%s'
PAGES = { URL_REGEXP: DisplayPage } PAGES = {URL_REGEXP: DisplayPage}

View file

@ -19,8 +19,9 @@
from weboob.tools.capabilities.gallery.genericcomicreader import GenericComicReaderTest from weboob.tools.capabilities.gallery.genericcomicreader import GenericComicReaderTest
class MangahereTest(GenericComicReaderTest): class MangahereTest(GenericComicReaderTest):
BACKEND = 'mangahere' BACKEND = 'mangahere'
def test_download(self): def test_download(self):
return self._test_download('glass_no_kamen/v02/c000') return self._test_download('glass_no_kamen/v02/c000')

View file

@ -19,8 +19,10 @@
from weboob.tools.capabilities.gallery.genericcomicreader import GenericComicReaderBackend, DisplayPage from weboob.tools.capabilities.gallery.genericcomicreader import GenericComicReaderBackend, DisplayPage
__all__ = ['MangareaderBackend'] __all__ = ['MangareaderBackend']
class MangareaderBackend(GenericComicReaderBackend): class MangareaderBackend(GenericComicReaderBackend):
NAME = 'mangareader' NAME = 'mangareader'
DESCRIPTION = 'MangaReader manga reading website' DESCRIPTION = 'MangaReader manga reading website'
@ -31,4 +33,4 @@ class MangareaderBackend(GenericComicReaderBackend):
ID_REGEXP = r'[^/]+/[^/]+' ID_REGEXP = r'[^/]+/[^/]+'
URL_REGEXP = r'.+mangareader.net/(%s).+' % ID_REGEXP URL_REGEXP = r'.+mangareader.net/(%s).+' % ID_REGEXP
ID_TO_URL = 'http://www.mangareader.net/%s' ID_TO_URL = 'http://www.mangareader.net/%s'
PAGES = { r'http://.+\.mangareader.net/.+': DisplayPage } # oh well PAGES = {r'http://.+\.mangareader.net/.+': DisplayPage} # oh well

View file

@ -19,8 +19,9 @@
from weboob.tools.capabilities.gallery.genericcomicreader import GenericComicReaderTest from weboob.tools.capabilities.gallery.genericcomicreader import GenericComicReaderTest
class MangareaderTest(GenericComicReaderTest): class MangareaderTest(GenericComicReaderTest):
BACKEND = 'mangareader' BACKEND = 'mangareader'
def test_download(self): def test_download(self):
return self._test_download('glass-mask/3') return self._test_download('glass-mask/3')

View file

@ -29,6 +29,7 @@ from .browser import MediawikiBrowser
__all__ = ['MediawikiBackend'] __all__ = ['MediawikiBackend']
class MediawikiBackend(BaseBackend, ICapContent): class MediawikiBackend(BaseBackend, ICapContent):
NAME = 'mediawiki' NAME = 'mediawiki'
MAINTAINER = u'Clément Schreiner' MAINTAINER = u'Clément Schreiner'
@ -42,6 +43,7 @@ class MediawikiBackend(BaseBackend, ICapContent):
ValueBackendPassword('password', label='Password', default='')) ValueBackendPassword('password', label='Password', default=''))
BROWSER = MediawikiBrowser BROWSER = MediawikiBrowser
def create_default_browser(self): def create_default_browser(self):
username = self.config['username'].get() username = self.config['username'].get()
if len(username) > 0: if len(username) > 0:

View file

@ -21,7 +21,6 @@ from urlparse import urlsplit
import urllib import urllib
import datetime import datetime
from weboob.tools.browser import BaseBrowser, BrowserIncorrectPassword from weboob.tools.browser import BaseBrowser, BrowserIncorrectPassword
from weboob.capabilities.content import Revision from weboob.capabilities.content import Revision
@ -37,6 +36,7 @@ __all__ = ['MediawikiBrowser']
class APIError(Exception): class APIError(Exception):
pass pass
# Browser # Browser
class MediawikiBrowser(BaseBrowser): class MediawikiBrowser(BaseBrowser):
ENCODING = 'utf-8' ENCODING = 'utf-8'
@ -63,8 +63,6 @@ class MediawikiBrowser(BaseBrowser):
'intoken': 'edit', 'intoken': 'edit',
} }
result = self.API_get(data) result = self.API_get(data)
pageid = result['query']['pages'].keys()[0] pageid = result['query']['pages'].keys()[0]
if pageid == "-1": # Page does not exist if pageid == "-1": # Page does not exist
@ -83,8 +81,7 @@ class MediawikiBrowser(BaseBrowser):
} }
result = self.API_get(data) result = self.API_get(data)
pageid = result['query']['pages'].keys()[0] pageid = result['query']['pages'].keys()[0]
return result['query']['pages'][str(pageid)][_type+'token'] return result['query']['pages'][str(pageid)][_type + 'token']
def set_wiki_source(self, content, message=None, minor=False): def set_wiki_source(self, content, message=None, minor=False):
if len(self.username) > 0 and not self.is_logged(): if len(self.username) > 0 and not self.is_logged():
@ -138,7 +135,9 @@ class MediawikiBrowser(BaseBrowser):
self.API_post(data) self.API_post(data)
def iter_wiki_revisions(self, page, nb_entries): def iter_wiki_revisions(self, page, nb_entries):
'''Yield 'Revision' objects for the last <nb_entries> revisions of the specified page.''' """
Yield 'Revision' objects for the last <nb_entries> revisions of the specified page.
"""
if len(self.username) > 0 and not self.is_logged(): if len(self.username) > 0 and not self.is_logged():
self.login() self.login()
data = {'action': 'query', data = {'action': 'query',
@ -158,14 +157,11 @@ class MediawikiBrowser(BaseBrowser):
rev_content.revision = str(rev['revid']) rev_content.revision = str(rev['revid'])
rev_content.author = rev['user'] rev_content.author = rev['user']
rev_content.timestamp = datetime.datetime.strptime(rev['timestamp'], '%Y-%m-%dT%H:%M:%SZ') rev_content.timestamp = datetime.datetime.strptime(rev['timestamp'], '%Y-%m-%dT%H:%M:%SZ')
if rev.has_key('minor'): rev_content.minor = 'minor' in rev
rev_content.minor = True
else:
rev_content.minor = False
yield rev_content yield rev_content
def home(self): def home(self):
'''We don't need to change location, we're using the JSON API here.''' # We don't need to change location, we're using the JSON API here.
pass pass
def check_result(self, result): def check_result(self, result):
@ -173,17 +169,20 @@ class MediawikiBrowser(BaseBrowser):
raise APIError('%s' % result['error']['info']) raise APIError('%s' % result['error']['info'])
def API_get(self, data): def API_get(self, data):
'''Submit a GET request to the website """
The JSON data is parsed and returned as a dictionary''' Submit a GET request to the website
The JSON data is parsed and returned as a dictionary
"""
data['format'] = 'json' data['format'] = 'json'
result = simplejson.loads(self.readurl(self.buildurl(self.apiurl, **data)), 'utf-8') result = simplejson.loads(self.readurl(self.buildurl(self.apiurl, **data)), 'utf-8')
self.check_result(result) self.check_result(result)
return result return result
def API_post(self, data): def API_post(self, data):
'''Submit a POST request to the website """
The JSON data is parsed and returned as a dictionary''' Submit a POST request to the website
The JSON data is parsed and returned as a dictionary
"""
data['format'] = 'json' data['format'] = 'json'
result = simplejson.loads(self.readurl(self.apiurl, urllib.urlencode(data)), 'utf-8') result = simplejson.loads(self.readurl(self.apiurl, urllib.urlencode(data)), 'utf-8')
self.check_result(result) self.check_result(result)

View file

@ -18,7 +18,6 @@
# along with weboob. If not, see <http://www.gnu.org/licenses/>. # along with weboob. If not, see <http://www.gnu.org/licenses/>.
from weboob.tools.browser import BasePage from weboob.tools.browser import BasePage
from weboob.capabilities.weather import Forecast, Current, City from weboob.capabilities.weather import Forecast, Current, City
@ -69,10 +68,10 @@ class WeatherPage(BasePage):
Return the city from the forecastpage. Return the city from the forecastpage.
""" """
for div in self.document.getiterator('div'): for div in self.document.getiterator('div'):
if div.attrib.has_key("class") and div.attrib.get("class") == "choix": if div.attrib.get("class", "") == "choix":
for strong in div.getiterator("strong"): for strong in div.getiterator("strong"):
city_name=strong.text +" "+ strong.tail.replace("(","").replace(")","") city_name = strong.text + " " + strong.tail.replace("(", "").replace(")", "")
city_id=self.url.split("/")[-1] city_id = self.url.split("/")[-1]
return City(city_id, city_name) return City(city_id, city_name)
@ -84,5 +83,5 @@ class CityPage(BasePage):
city_name = li.text_content() city_name = li.text_content()
for children in li.getchildren(): for children in li.getchildren():
city_id = children.attrib.get("href").split("/")[-1] city_id = children.attrib.get("href").split("/")[-1]
mcity = City( city_id, city_name) mcity = City(city_id, city_name)
yield mcity yield mcity

View file

@ -23,6 +23,7 @@ from weboob.tools.capabilities.messages.GenericBackend import GenericNewspaperBa
from .browser import Newspaper20minutesBrowser from .browser import Newspaper20minutesBrowser
from .tools import rssid from .tools import rssid
class Newspaper20minutesBackend(GenericNewspaperBackend, ICapMessages): class Newspaper20minutesBackend(GenericNewspaperBackend, ICapMessages):
MAINTAINER = 'Julien Hebert' MAINTAINER = 'Julien Hebert'
EMAIL = 'juke@free.fr' EMAIL = 'juke@free.fr'
@ -34,4 +35,3 @@ class Newspaper20minutesBackend(GenericNewspaperBackend, ICapMessages):
BROWSER = Newspaper20minutesBrowser BROWSER = Newspaper20minutesBrowser
RSS_FEED = 'http://www.20minutes.fr/rss/20minutes.xml' RSS_FEED = 'http://www.20minutes.fr/rss/20minutes.xml'
RSSID = rssid RSSID = rssid

View file

@ -21,6 +21,7 @@
from weboob.tools.capabilities.messages.genericArticle import NoAuthorElement, try_remove, NoneMainDiv from weboob.tools.capabilities.messages.genericArticle import NoAuthorElement, try_remove, NoneMainDiv
from .simple import SimplePage from .simple import SimplePage
class ArticlePage(SimplePage): class ArticlePage(SimplePage):
"ArticlePage object for minutes20" "ArticlePage object for minutes20"
def on_loaded(self): def on_loaded(self):
@ -37,9 +38,8 @@ class ArticlePage(SimplePage):
else: else:
try_remove(self.parser, element_body, "div.mna-tools") try_remove(self.parser, element_body, "div.mna-tools")
try_remove(self.parser, element_body, "div.mna-comment-call") try_remove(self.parser, element_body, "div.mna-comment-call")
try : try:
element_body.remove(self.get_element_author()) element_body.remove(self.get_element_author())
except NoAuthorElement: except NoAuthorElement:
pass pass
return self.parser.tostring(element_body) return self.parser.tostring(element_body)

View file

@ -20,6 +20,7 @@
from weboob.tools.capabilities.messages.genericArticle import GenericNewsPage from weboob.tools.capabilities.messages.genericArticle import GenericNewsPage
class SimplePage(GenericNewsPage): class SimplePage(GenericNewsPage):
"ArticlePage object for minutes20" "ArticlePage object for minutes20"
def on_loaded(self): def on_loaded(self):
@ -27,4 +28,3 @@ class SimplePage(GenericNewsPage):
self.element_title_selector = "h1" self.element_title_selector = "h1"
self.element_author_selector = "div.mna-signature" self.element_author_selector = "div.mna-signature"
self.element_body_selector = "div.mna-body" self.element_body_selector = "div.mna-body"

View file

@ -18,24 +18,27 @@
# 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/>.
import re import re
def id2url(_id): def id2url(_id):
"return an url from an id" "return an url from an id"
regexp2 = re.compile("(\w+).([0-9]+).(.*$)") regexp2 = re.compile("(\w+).([0-9]+).(.*$)")
match = regexp2.match(_id) match = regexp2.match(_id)
if match: if match:
return 'http://www.20minutes.fr/%s/%s/%s' % ( match.group(1), return 'http://www.20minutes.fr/%s/%s/%s' % (match.group(1),
match.group(2), match.group(2),
match.group(3)) match.group(3))
else: else:
raise ValueError("id doesn't match") raise ValueError("id doesn't match")
def url2id(url): def url2id(url):
"return an id from an url" "return an id from an url"
regexp = re.compile("http://www.20minutes.fr/(\w+)/([0-9]+)/(.*$)") regexp = re.compile("http://www.20minutes.fr/(\w+)/([0-9]+)/(.*$)")
match = regexp.match(url) match = regexp.match(url)
return '%s.%d.%s' % (match.group(1), int(match.group(2)), match.group(3)) return '%s.%d.%s' % (match.group(1), int(match.group(2)), match.group(3))
def rssid(entry): def rssid(entry):
return url2id(entry.id) return url2id(entry.id)

View file

@ -20,10 +20,10 @@
from weboob.tools.test import BackendTest from weboob.tools.test import BackendTest
class NewsfeedTest(BackendTest): class NewsfeedTest(BackendTest):
BACKEND = 'newsfeed' BACKEND = 'newsfeed'
def test_newsfeed(self): def test_newsfeed(self):
for message in self.backend.iter_unread_messages(): for message in self.backend.iter_unread_messages():
pass pass

View file

@ -35,8 +35,8 @@ __all__ = ['VideoPage']
class ForbiddenVideo(Exception): class ForbiddenVideo(Exception):
pass pass
class VideoPage(BasePage):
class VideoPage(BasePage):
def get_video(self, video=None): def get_video(self, video=None):
_id = to_unicode(self.group_dict['id']) _id = to_unicode(self.group_dict['id'])
if video is None: if video is None:
@ -84,4 +84,3 @@ class VideoPage(BasePage):
video.url = values['url'] video.url = values['url']
return video return video

View file

@ -67,4 +67,3 @@ class AloesBackend(BaseBackend, ICapBook):
def search_books(self, _string): def search_books(self, _string):
raise NotImplementedError() raise NotImplementedError()

View file

@ -42,7 +42,7 @@ class AloesBrowser(BaseBrowser):
} }
def __init__(self, baseurl, *args, **kwargs): def __init__(self, baseurl, *args, **kwargs):
self.BASEURL=baseurl self.BASEURL = baseurl
BaseBrowser.__init__(self, *args, **kwargs) BaseBrowser.__init__(self, *args, **kwargs)
def is_logged(self): def is_logged(self):
@ -60,10 +60,9 @@ class AloesBrowser(BaseBrowser):
no_login=True) no_login=True)
if not self.page.login(self.username, self.password) or \ if not self.page.login(self.username, self.password) or \
not self.is_logged() or \ not self.is_logged() or \
(self.is_on_page(LoginPage) and self.page.is_error()) : (self.is_on_page(LoginPage) and self.page.is_error()):
raise BrowserIncorrectPassword() raise BrowserIncorrectPassword()
def get_rented_books_list(self): def get_rented_books_list(self):
if not self.is_on_page(RentedPage): if not self.is_on_page(RentedPage):
self.location('%s://%s/index.aspx?IdPage=45' \ self.location('%s://%s/index.aspx?IdPage=45' \
@ -73,6 +72,6 @@ class AloesBrowser(BaseBrowser):
def get_booked_books_list(self): def get_booked_books_list(self):
if not self.is_on_page(BookedPage): if not self.is_on_page(BookedPage):
self.location('%s://%s/index.aspx?IdPage=44' \ self.location('%s://%s/index.aspx?IdPage=44' \
% (self.PROTOCOL, self.BASEURL)) % (self.PROTOCOL, self.BASEURL))
return self.page.get_list() return self.page.get_list()

View file

@ -24,6 +24,7 @@ from weboob.tools.browser import BasePage
__all__ = ['ComposePage', 'ConfirmPage'] __all__ = ['ComposePage', 'ConfirmPage']
class ConfirmPage(BasePage): class ConfirmPage(BasePage):
def on_loaded(self): def on_loaded(self):
pass pass
@ -33,9 +34,9 @@ class ComposePage(BasePage):
phone_regex = re.compile('^(\+33|0033|0)(6|7)(\d{8})$') phone_regex = re.compile('^(\+33|0033|0)(6|7)(\d{8})$')
def on_loaded(self): def on_loaded(self):
#Deal with bad encoding... for ie6 ... # Deal with bad encoding... for ie6...
response = self.browser.response() response = self.browser.response()
response.set_data(response.get_data().decode('utf-8', 'ignore') ) response.set_data(response.get_data().decode('utf-8', 'ignore'))
self.browser.set_response(response) self.browser.set_response(response)
def get_nb_remaining_free_sms(self): def get_nb_remaining_free_sms(self):
@ -46,12 +47,12 @@ class ComposePage(BasePage):
if self.phone_regex.match(receiver) is None: if self.phone_regex.match(receiver) is None:
raise CantSendMessage(u'Invalid receiver: %s' % receiver) raise CantSendMessage(u'Invalid receiver: %s' % receiver)
listetel = ",,"+ receiver listetel = ",," + receiver
#Fill the form #Fill the form
self.browser.select_form(name="formulaire") self.browser.select_form(name="formulaire")
self.browser.new_control("hidden", "autorize",{'value':''}) self.browser.new_control("hidden", "autorize", {'value': ''})
self.browser.new_control("textarea", "msg", {'value':''}) self.browser.new_control("textarea", "msg", {'value': ''})
self.browser.set_all_readonly(False) self.browser.set_all_readonly(False)

View file

@ -43,8 +43,8 @@ class PastealaconBackend(BaseBackend, BasePasteBackend):
BROWSER = PastealaconBrowser BROWSER = PastealaconBrowser
EXPIRATIONS = { EXPIRATIONS = {
24*3600: 'd', 24 * 3600: 'd',
24*3600*30: 'm', 24 * 3600 * 30: 'm',
False: 'f', False: 'f',
} }
@ -83,7 +83,7 @@ class PastealaconBackend(BaseBackend, BasePasteBackend):
self.browser.fill_paste(paste) self.browser.fill_paste(paste)
return paste return paste
def post_paste(self, paste, max_age = None): def post_paste(self, paste, max_age=None):
if max_age is not None: if max_age is not None:
expiration = self.get_closest_expiration(max_age) expiration = self.get_closest_expiration(max_age)
else: else:

View file

@ -21,6 +21,7 @@ from weboob.tools.capabilities.gallery.genericcomicreader import GenericComicRea
__all__ = ['SimplyreaditBackend'] __all__ = ['SimplyreaditBackend']
class SimplyreaditBackend(GenericComicReaderBackend): class SimplyreaditBackend(GenericComicReaderBackend):
NAME = 'simplyreadit' NAME = 'simplyreadit'
DESCRIPTION = 'SimplyReadIt manga reading website' DESCRIPTION = 'SimplyReadIt manga reading website'
@ -30,4 +31,4 @@ class SimplyreaditBackend(GenericComicReaderBackend):
ID_TO_URL = 'http://www.simplyread.it/reader/read/%s' ID_TO_URL = 'http://www.simplyread.it/reader/read/%s'
ID_REGEXP = r'[^/]+(?:/[^/]+)*' ID_REGEXP = r'[^/]+(?:/[^/]+)*'
URL_REGEXP = r'.+simplyread.it/reader/read/(%s)/page/.+' % ID_REGEXP URL_REGEXP = r'.+simplyread.it/reader/read/(%s)/page/.+' % ID_REGEXP
PAGES = { r'http://.+\.simplyread.it/reader/read/.+': DisplayPage } PAGES = {r'http://.+\.simplyread.it/reader/read/.+': DisplayPage}

View file

@ -19,8 +19,9 @@
from weboob.tools.capabilities.gallery.genericcomicreader import GenericComicReaderTest from weboob.tools.capabilities.gallery.genericcomicreader import GenericComicReaderTest
class SimplyreaditTest(GenericComicReaderTest): class SimplyreaditTest(GenericComicReaderTest):
BACKEND = 'simplyreadit' BACKEND = 'simplyreadit'
def test_download(self): def test_download(self):
return self._test_download('bonnouji/en/1/3') return self._test_download('bonnouji/en/1/3')

View file

@ -21,18 +21,20 @@
import hashlib import hashlib
import Image import Image
class TileError(Exception): class TileError(Exception):
def __init__(self, msg, tile = None): def __init__(self, msg, tile=None):
Exception.__init__(self, msg) Exception.__init__(self, msg)
self.tile = tile self.tile = tile
class Captcha:
class Captcha(object):
def __init__(self, file, infos): def __init__(self, file, infos):
self.inim = Image.open(file) self.inim = Image.open(file)
self.infos = infos self.infos = infos
self.nbr = int(infos["nblignes"]) self.nbr = int(infos["nblignes"])
self.nbc = int(infos["nbcolonnes"]) self.nbc = int(infos["nbcolonnes"])
(self.nx,self.ny) = self.inim.size (self.nx, self.ny) = self.inim.size
self.inmat = self.inim.load() self.inmat = self.inim.load()
self.map = {} self.map = {}
@ -77,7 +79,7 @@ class Captcha:
self.map[num] = tile self.map[num] = tile
class Tile: class Tile(object):
hash = {'ff1441b2c5f90703ef04e688e399aca5': 1, hash = {'ff1441b2c5f90703ef04e688e399aca5': 1,
'53d7f3dfd64f54723b231fc398b6be57': 2, '53d7f3dfd64f54723b231fc398b6be57': 2,
'5bcba7fa2107ba9a606e8d0131c162eb': 3, '5bcba7fa2107ba9a606e8d0131c162eb': 3,
@ -116,4 +118,3 @@ class Tile:
def display(self): def display(self):
print self.checksum() print self.checksum()

View file

@ -21,7 +21,9 @@
from .accounts_list import AccountsList from .accounts_list import AccountsList
from .login import LoginPage, BadLoginPage from .login import LoginPage, BadLoginPage
class AccountPrelevement(AccountsList): pass
class AccountPrelevement(AccountsList):
pass
__all__ = ['LoginPage', __all__ = ['LoginPage',
'BadLoginPage', 'BadLoginPage',

View file

@ -53,11 +53,11 @@ class LoginPage(BasePage):
infos_xml = etree.XML(infos_data) infos_xml = etree.XML(infos_data)
infos = {} infos = {}
for el in ("cryptogramme", "nblignes", "nbcolonnes"): for el in ("cryptogramme", "nblignes", "nbcolonnes"):
infos[el] = infos_xml.find(el).text infos[el] = infos_xml.find(el).text
infos["grille"] = "" infos["grille"] = ""
for g in infos_xml.findall("grille"): for g in infos_xml.findall("grille"):
infos["grille"] += g.text + "," infos["grille"] += g.text + ","
infos["keyCodes"] = infos["grille"].split(",") infos["keyCodes"] = infos["grille"].split(",")
url = base_url + '/cvcsgenimage?modeClavier=0&cryptogramme=' + infos["cryptogramme"] url = base_url + '/cvcsgenimage?modeClavier=0&cryptogramme=' + infos["cryptogramme"]
@ -79,5 +79,6 @@ class LoginPage(BasePage):
self.browser['cryptocvcs'] = infos["cryptogramme"] self.browser['cryptocvcs'] = infos["cryptogramme"]
self.browser.submit() self.browser.submit()
class BadLoginPage(BasePage): class BadLoginPage(BasePage):
pass pass

View file

@ -16,6 +16,3 @@
# #
# 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/>.

View file

@ -63,4 +63,3 @@ class VideoPage(BasePage):
video.url = video_file_urls[0] video.url = video_file_urls[0]
return video return video

View file

@ -44,6 +44,7 @@ class YoupornBackend(BaseBackend, ICapVideo):
return self.browser.get_video(_id) return self.browser.get_video(_id)
SORTBY = ['relevance', 'rating', 'views', 'time'] SORTBY = ['relevance', 'rating', 'views', 'time']
def search_videos(self, pattern=None, sortby=ICapVideo.SEARCH_RELEVANCE, nsfw=False, max_results=None): def search_videos(self, pattern=None, sortby=ICapVideo.SEARCH_RELEVANCE, nsfw=False, max_results=None):
if not nsfw: if not nsfw:
return set() return set()

View file

@ -47,6 +47,7 @@ class LoginPage(BasePage):
self.browser['Passwd'] = password self.browser['Passwd'] = password
self.browser.submit() self.browser.submit()
class LoginRedirectPage(BasePage): class LoginRedirectPage(BasePage):
pass pass
@ -64,6 +65,7 @@ class BaseYoutubePage(BasePage):
else: else:
return True return True
class ForbiddenVideoPage(BaseYoutubePage): class ForbiddenVideoPage(BaseYoutubePage):
def on_loaded(self): def on_loaded(self):
element = self.parser.select(self.document.getroot(), '.yt-alert-content', 1) element = self.parser.select(self.document.getroot(), '.yt-alert-content', 1)
@ -78,11 +80,13 @@ class VerifyAgePage(BaseYoutubePage):
self.browser.select_form(predicate=lambda form: form.attrs.get('id', '') == 'confirm-age-form') self.browser.select_form(predicate=lambda form: form.attrs.get('id', '') == 'confirm-age-form')
self.browser.submit() self.browser.submit()
class VerifyControversyPage(BaseYoutubePage): class VerifyControversyPage(BaseYoutubePage):
def on_loaded(self): def on_loaded(self):
self.browser.select_form(predicate=lambda form: 'verify_controversy' in form.attrs.get('action', '')) self.browser.select_form(predicate=lambda form: 'verify_controversy' in form.attrs.get('action', ''))
self.browser.submit() self.browser.submit()
class VideoPage(BaseYoutubePage): class VideoPage(BaseYoutubePage):
AVAILABLE_FORMATS = [38, 37, 45, 22, 43, 35, 34, 18, 6, 5, 17, 13] AVAILABLE_FORMATS = [38, 37, 45, 22, 43, 35, 34, 18, 6, 5, 17, 13]
FORMAT_EXTENSIONS = { FORMAT_EXTENSIONS = {
@ -91,7 +95,7 @@ class VideoPage(BaseYoutubePage):
18: 'mp4', 18: 'mp4',
22: 'mp4', 22: 'mp4',
37: 'mp4', 37: 'mp4',
38: 'video', # You actually don't know if this will be MOV, AVI or whatever 38: 'video', # You actually don't know if this will be MOV, AVI or whatever
43: 'webm', 43: 'webm',
45: 'webm', 45: 'webm',
} }

View file

@ -38,7 +38,7 @@ def check_executable_win(executable, error):
path = filter(None, path.split(";")) path = filter(None, path.split(";"))
if dotfirst: if dotfirst:
path = ["."]+path path = ["."] + path
pathext = os.environ[pathextsrc] pathext = os.environ[pathextsrc]
pathext = filter(None, pathext.split(";")) pathext = filter(None, pathext.split(";"))
@ -56,13 +56,14 @@ def check_executable_win(executable, error):
for e in pathext: for e in pathext:
filePath = os.path.join(d, cmdName + e) filePath = os.path.join(d, cmdName + e)
if os.path.exists(filePath): if os.path.exists(filePath):
return filePath.replace( '\\', '/' ) return filePath.replace('\\', '/')
print >>sys.stderr, 'Error: %s is not installed on your system.' % executable print >>sys.stderr, 'Error: %s is not installed on your system.' % executable
if error: if error:
print >>sys.stderr, error print >>sys.stderr, error
sys.exit(1) sys.exit(1)
def check_executable_unix(executable, error): def check_executable_unix(executable, error):
with open('/dev/null', 'w') as devnull: with open('/dev/null', 'w') as devnull:
process = subprocess.Popen(['which', executable], stdout=devnull) process = subprocess.Popen(['which', executable], stdout=devnull)
@ -80,28 +81,31 @@ if sys.platform == 'win32':
else: else:
check_executable = check_executable_unix check_executable = check_executable_unix
def build_qt(): def build_qt():
print 'Building Qt applications' print 'Building Qt applications'
pyuic4 = check_executable('pyuic4', 'Install PyQt4-devel or disable Qt applications (with --no-qt).') pyuic4 = check_executable('pyuic4', 'Install PyQt4-devel or disable Qt applications (with --no-qt).')
if sys.platform == 'win32': if sys.platform == 'win32':
env={ 'PYUIC' : pyuic4, 'PATH':os.environ['PATH']} env = {'PYUIC': pyuic4, 'PATH': os.environ['PATH']}
extraMakeFlag = ['-e'] extraMakeFlag = ['-e']
else: else:
env = None env = None
extraMakeFlag = [] extraMakeFlag = []
subprocess.check_call(['make']+extraMakeFlag+['-C','weboob/applications/qboobmsg/ui'], env=env ) subprocess.check_call(['make']+extraMakeFlag+['-C', 'weboob/applications/qboobmsg/ui'], env=env)
subprocess.check_call(['make']+extraMakeFlag+['-C','weboob/applications/qhavedate/ui'], env=env ) subprocess.check_call(['make']+extraMakeFlag+['-C', 'weboob/applications/qhavedate/ui'], env=env)
if sys.platform != 'win32': if sys.platform != 'win32':
subprocess.check_call(['make']+extraMakeFlag+['-C','weboob/applications/qvideoob/ui'], env=env ) subprocess.check_call(['make']+extraMakeFlag+['-C', 'weboob/applications/qvideoob/ui'], env=env)
subprocess.check_call(['make']+extraMakeFlag+['-C','weboob/applications/qwebcontentedit/ui'], env=env ) subprocess.check_call(['make']+extraMakeFlag+['-C', 'weboob/applications/qwebcontentedit/ui'], env=env)
subprocess.check_call(['make']+extraMakeFlag+['-C','weboob/applications/qflatboob/ui'], env=env ) subprocess.check_call(['make']+extraMakeFlag+['-C', 'weboob/applications/qflatboob/ui'], env=env)
subprocess.check_call(['make']+extraMakeFlag+['-C','weboob/tools/application/qt'], env=env ) subprocess.check_call(['make']+extraMakeFlag+['-C', 'weboob/tools/application/qt'], env=env)
class Options:
class Options(object):
pass pass
options = Options() options = Options()
options.hildon = False options.hildon = False
options.qt = True options.qt = True
@ -189,7 +193,7 @@ if options.xdg:
setup( setup(
name='weboob', name='weboob',
version = '0.b', version='0.b',
description='Weboob, Web Out Of Browsers', description='Weboob, Web Out Of Browsers',
long_description=open('README').read(), long_description=open('README').read(),
author='Romain Bignon', author='Romain Bignon',

View file

@ -20,8 +20,11 @@
from __future__ import with_statement from __future__ import with_statement
import sys, os, tempfile import sys
import imp, inspect import os
import tempfile
import imp
import inspect
import optparse import optparse
import re import re
import time import time
@ -31,8 +34,9 @@ from weboob.tools.application.base import BaseApplication
BASE_PATH = os.path.join(os.path.dirname(__file__), os.pardir) BASE_PATH = os.path.join(os.path.dirname(__file__), os.pardir)
DEST_DIR = 'man' DEST_DIR = 'man'
class ManpageHelpFormatter(optparse.HelpFormatter): class ManpageHelpFormatter(optparse.HelpFormatter):
def __init__ (self, def __init__(self,
app, app,
indent_increment=0, indent_increment=0,
max_help_position=0, max_help_position=0,
@ -94,7 +98,7 @@ class ManpageHelpFormatter(optparse.HelpFormatter):
def format_option_strings(self, option): def format_option_strings(self, option):
opts = optparse.HelpFormatter.format_option_strings(self, option).split(", ") opts = optparse.HelpFormatter.format_option_strings(self, option).split(", ")
return ".TP\n"+", ".join("\\fB%s\\fR" % opt for opt in opts) return ".TP\n" + ", ".join("\\fB%s\\fR" % opt for opt in opts)
def main(): def main():
@ -129,17 +133,21 @@ def main():
analyze_application(klass, fname) analyze_application(klass, fname)
finally: finally:
# Cleanup compiled files if needed # Cleanup compiled files if needed
if (os.path.isfile(tmpfile+"c")): if (os.path.isfile(tmpfile + "c")):
os.unlink(tmpfile+"c") os.unlink(tmpfile + "c")
def format_title(title): def format_title(title):
return re.sub(r'^(.+):$', r'.SH \1\n.TP', title.group().upper()) return re.sub(r'^(.+):$', r'.SH \1\n.TP', title.group().upper())
# XXX useful because the PyQt QApplication destructor crashes sometimes. By # XXX useful because the PyQt QApplication destructor crashes sometimes. By
# keeping every applications until program end, it prevents to stop before # keeping every applications until program end, it prevents to stop before
# every manpages have been generated. If it crashes at exit, it's not a # every manpages have been generated. If it crashes at exit, it's not a
# really a problem. # really a problem.
applications = [] applications = []
def analyze_application(app, script_name): def analyze_application(app, script_name):
application = app() application = app()
applications.append(application) applications.append(application)

View file

@ -1 +0,0 @@

View file

@ -1 +0,0 @@

View file

@ -49,6 +49,7 @@ class QifFormatter(IFormatter):
self.count += 1 self.count += 1
return result return result
class TransactionsFormatter(IFormatter): 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']
@ -82,6 +83,7 @@ class TransactionsFormatter(IFormatter):
result += ' %-10s %-12s %-50s %10.2f' % (item['date'].strftime('%Y-%m-%d'), _type, label[:50], item['amount']) result += ' %-10s %-12s %-50s %10.2f' % (item['date'].strftime('%Y-%m-%d'), _type, label[:50], item['amount'])
return result return result
class TransferFormatter(IFormatter): class TransferFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'date', 'origin', 'recipient', 'amount') MANDATORY_FIELDS = ('id', 'date', 'origin', 'recipient', 'amount')
@ -96,6 +98,7 @@ class TransferFormatter(IFormatter):
result += u'Amount: %.2f\n' % item['amount'] result += u'Amount: %.2f\n' % item['amount']
return result return result
class RecipientListFormatter(IFormatter): class RecipientListFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'label') MANDATORY_FIELDS = ('id', 'label')
@ -115,6 +118,7 @@ class RecipientListFormatter(IFormatter):
return u'%s %-30s %s %s' % (self.BOLD, id, self.NC, item['label']) 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')
@ -122,13 +126,12 @@ class AccountListFormatter(IFormatter):
tot_balance = 0.0 tot_balance = 0.0
tot_coming = 0.0 tot_coming = 0.0
def flush(self): def flush(self):
if self.count < 1: if self.count < 1:
return return
result = u'------------------------------------------%s+----------+----------\n' % (('-' * 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 '', result += u'%s Total %8s %8s' % ((' ' * 15) if not self.interactive else '',
'%.2f' % self.tot_balance, '%.2f' % self.tot_coming) '%.2f' % self.tot_balance, '%.2f' % self.tot_coming)
self.after_format(result) self.after_format(result)
self.tot_balance = 0.0 self.tot_balance = 0.0
@ -156,6 +159,7 @@ class AccountListFormatter(IFormatter):
self.tot_coming += item['coming'] self.tot_coming += item['coming']
return result return result
class Boobank(ReplApplication): class Boobank(ReplApplication):
APPNAME = 'boobank' APPNAME = 'boobank'
VERSION = '0.b' VERSION = '0.b'
@ -278,7 +282,7 @@ class Boobank(ReplApplication):
try: try:
amount = float(amount) amount = float(amount)
except (TypeError,ValueError): except (TypeError, ValueError):
print >>sys.stderr, 'Error: please give a decimal amount to transfer' print >>sys.stderr, 'Error: please give a decimal amount to transfer'
return 2 return 2

View file

@ -124,7 +124,7 @@ class MasstransitHildon():
self.main_window.connect("destroy", self.on_main_window_destroy) self.main_window.connect("destroy", self.on_main_window_destroy)
self.refresh_button.connect("clicked", self.on_refresh_button_clicked) self.refresh_button.connect("clicked", self.on_refresh_button_clicked)
self.retour_button.set_sensitive(False) self.retour_button.set_sensitive(False)
self.retour_button.connect("clicked", self.on_retour_button_clicked) self.retour_button.connect("clicked", self.on_retour_button_clicked)
@ -165,8 +165,8 @@ class MasstransitHildon():
text=4 text=4
)) ))
vertical_box = gtk.VBox() vertical_box = gtk.VBox()
vertical_box.pack_start(horizontal_box) vertical_box.pack_start(horizontal_box)
horizontal_box.pack_start(self.retour_button) horizontal_box.pack_start(self.retour_button)
@ -187,7 +187,7 @@ class MasstransitHildon():
def fill_touch_selector_entry(self): def fill_touch_selector_entry(self):
liste = [] liste = []
for backend in self.weboob.iter_backends(): for backend in self.weboob.iter_backends():
for station in backend.iter_station_search(""): for station in backend.iter_station_search(""):
liste.append(station.name.capitalize()) liste.append(station.name.capitalize())
@ -272,7 +272,7 @@ class MasstransitHildon():
self.refresh_in_progress = False self.refresh_in_progress = False
banner.set_timeout(1) banner.set_timeout(1)
hildon.hildon_gtk_window_set_progress_indicator(self.main_window, 0) hildon.hildon_gtk_window_set_progress_indicator(self.main_window, 0)
class Masstransit(BaseApplication): class Masstransit(BaseApplication):

View file

@ -1,3 +1,4 @@
from .qwebcontentedit import QWebContentEdit from .qwebcontentedit import QWebContentEdit
__all__ = ['QWebContentEdit']
__all__ = ['QWebContentEdit']

View file

@ -22,6 +22,7 @@ from weboob.capabilities.content import ICapContent
from .main_window import MainWindow from .main_window import MainWindow
class QWebContentEdit(QtApplication): class QWebContentEdit(QtApplication):
APPNAME = 'qwebcontentedit' APPNAME = 'qwebcontentedit'
VERSION = '0.b' VERSION = '0.b'
@ -34,5 +35,3 @@ class QWebContentEdit(QtApplication):
self.main_window = MainWindow(self.config, self.weboob) self.main_window = MainWindow(self.config, self.weboob)
self.main_window.show() self.main_window.show()
return self.weboob.loop() return self.weboob.loop()

View file

@ -124,8 +124,8 @@ class WeboobRepos(ReplApplication):
'--keyring', os.path.realpath(krname), '--keyring', os.path.realpath(krname),
'--import', os.path.realpath(keypath)]) '--import', os.path.realpath(keypath)])
# Does not make much sense in our case # Does not make much sense in our case
if os.path.exists(krname+'~'): if os.path.exists(krname + '~'):
os.remove(krname+'~') os.remove(krname + '~')
if not os.path.exists(krname): if not os.path.exists(krname):
raise Exception('No valid key file found.') raise Exception('No valid key file found.')
kr_mtime = mktime(strptime(str(r.key_update), '%Y%m%d%H%M')) kr_mtime = mktime(strptime(str(r.key_update), '%Y%m%d%H%M'))
@ -134,7 +134,6 @@ class WeboobRepos(ReplApplication):
else: else:
print 'Keyring is up to date' print 'Keyring is up to date'
for name, module in r.modules.iteritems(): for name, module in r.modules.iteritems():
tarname = os.path.join(repo_path, '%s.tar.gz' % name) tarname = os.path.join(repo_path, '%s.tar.gz' % name)
if r.signed: if r.signed:
@ -185,7 +184,7 @@ class WeboobRepos(ReplApplication):
# Check if all files have an up to date signature # Check if all files have an up to date signature
for filename in sigfiles: for filename in sigfiles:
filepath = os.path.realpath(os.path.join(repo_path, filename)) filepath = os.path.realpath(os.path.join(repo_path, filename))
sigpath = filepath+'.sig' sigpath = filepath + '.sig'
file_mtime = int(os.path.getmtime(filepath)) file_mtime = int(os.path.getmtime(filepath))
if os.path.exists(sigpath): if os.path.exists(sigpath):
sig_mtime = int(os.path.getmtime(sigpath)) sig_mtime = int(os.path.getmtime(sigpath))
@ -202,7 +201,6 @@ class WeboobRepos(ReplApplication):
os.utime(sigpath, (file_mtime, file_mtime)) os.utime(sigpath, (file_mtime, file_mtime))
print 'Signatures are up to date' print 'Signatures are up to date'
@staticmethod @staticmethod
def _find_gpg(): def _find_gpg():
if os.getenv('GPG_EXECUTABLE'): if os.getenv('GPG_EXECUTABLE'):
@ -221,4 +219,3 @@ class WeboobRepos(ReplApplication):
if filename.endswith('.png'): if filename.endswith('.png'):
return True return True
return False return False

View file

@ -64,6 +64,7 @@ class NotLoaded(object):
class IBaseCap(object): class IBaseCap(object):
pass pass
class CapBaseObject(object): class CapBaseObject(object):
FIELDS = None FIELDS = None
_attribs = None _attribs = None
@ -128,7 +129,7 @@ class CapBaseObject(object):
if self.FIELDS is None: if self.FIELDS is None:
yield 'id', self.id yield 'id', self.id
for key, value in iter_fields(self): for key, value in iter_fields(self):
if key not in ('id', 'backend','FIELDS'): if key not in ('id', 'backend', 'FIELDS'):
yield key, value yield key, value
else: else:
yield 'id', self.id yield 'id', self.id

View file

@ -24,6 +24,7 @@ from weboob.tools.ordereddict import OrderedDict
__all__ = ['ICapContact', 'Contact'] __all__ = ['ICapContact', 'Contact']
class ProfileNode(object): class ProfileNode(object):
HEAD = 0x01 HEAD = 0x01
SECTION = 0x02 SECTION = 0x02
@ -38,6 +39,7 @@ class ProfileNode(object):
def __getitem__(self, key): def __getitem__(self, key):
return self.value[key] return self.value[key]
class ContactPhoto(CapBaseObject): class ContactPhoto(CapBaseObject):
def __init__(self, name): def __init__(self, name):
CapBaseObject.__init__(self, name) CapBaseObject.__init__(self, name)
@ -59,6 +61,7 @@ class ContactPhoto(CapBaseObject):
len(self.data) if self.data else 0, len(self.data) if self.data else 0,
len(self.thumbnail_data) if self.thumbnail_data else 0) len(self.thumbnail_data) if self.thumbnail_data else 0)
class Contact(CapBaseObject): class Contact(CapBaseObject):
STATUS_ONLINE = 0x001 STATUS_ONLINE = 0x001
STATUS_AWAY = 0x002 STATUS_AWAY = 0x002
@ -83,14 +86,17 @@ class Contact(CapBaseObject):
for key, value in kwargs.iteritems(): for key, value in kwargs.iteritems():
setattr(photo, key, value) setattr(photo, key, value)
class QueryError(Exception): class QueryError(Exception):
pass pass
class Query(CapBaseObject): class Query(CapBaseObject):
def __init__(self, id, message): def __init__(self, id, message):
CapBaseObject.__init__(self, id) CapBaseObject.__init__(self, id)
self.add_field('message', basestring, message) self.add_field('message', basestring, message)
class ICapContact(IBaseCap): class ICapContact(IBaseCap):
def iter_contacts(self, status=Contact.STATUS_ALL, ids=None): def iter_contacts(self, status=Contact.STATUS_ALL, ids=None):
""" """
@ -129,20 +135,20 @@ class ICapContact(IBaseCap):
@except QueryError @except QueryError
""" """
raise NotImplementedError() raise NotImplementedError()
def get_notes(self, id): def get_notes(self, id):
""" """
Get personal notes about a contact Get personal notes about a contact
@param id the ID of the contact @param id the ID of the contact
@return a unicode object @return a unicode object
""" """
raise NotImplementedError raise NotImplementedError
def save_notes(self, id, notes): def save_notes(self, id, notes):
""" """
Set personal notes about a contact Set personal notes about a contact
@param id the ID of the contact @param id the ID of the contact
@param notes the unicode object to save as notes @param notes the unicode object to save as notes
""" """

View file

@ -28,9 +28,11 @@ from logging import warning
__all__ = ['BackendsConfig', 'BackendAlreadyExists'] __all__ = ['BackendsConfig', 'BackendAlreadyExists']
class BackendAlreadyExists(Exception): class BackendAlreadyExists(Exception):
pass pass
class BackendsConfig(object): class BackendsConfig(object):
class WrongPermissions(Exception): class WrongPermissions(Exception):
pass pass
@ -41,13 +43,13 @@ class BackendsConfig(object):
mode = os.stat(confpath).st_mode mode = os.stat(confpath).st_mode
except OSError: except OSError:
if sys.platform == 'win32': if sys.platform == 'win32':
fptr = open(confpath,'w') fptr = open(confpath, 'w')
fptr.close() fptr.close()
else: else:
try: try:
os.mknod(confpath, 0600) os.mknod(confpath, 0600)
except OSError: except OSError:
fptr = open(confpath,'w') fptr = open(confpath, 'w')
fptr.close() fptr.close()
os.chmod(confpath, 0600) os.chmod(confpath, 0600)
else: else:
@ -129,5 +131,3 @@ class BackendsConfig(object):
with open(self.confpath, 'w') as f: with open(self.confpath, 'w') as f:
config.write(f) config.write(f)
return True return True

View file

@ -356,7 +356,7 @@ class Versions(object):
with open(os.path.join(self.path, self.VERSIONS_LIST), 'wb') as fp: with open(os.path.join(self.path, self.VERSIONS_LIST), 'wb') as fp:
config.write(fp) config.write(fp)
class IProgress: class IProgress(object):
def progress(self, percent, message): def progress(self, percent, message):
print '=== [%3.0f%%] %s' % (percent*100, message) print '=== [%3.0f%%] %s' % (percent*100, message)

View file

@ -1 +0,0 @@

View file

@ -1 +0,0 @@

View file

@ -92,7 +92,7 @@ class IFormatter(object):
if sys.platform == 'win32': if sys.platform == 'win32':
self.termrows = WConio.gettextinfo()[8] self.termrows = WConio.gettextinfo()[8]
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 after_format(self, formatted):
if self.outfile != sys.stdout: if self.outfile != sys.stdout:

View file

@ -36,9 +36,9 @@ class WebBrowser(gtk.Window):
self.connect('destroy', gtk.main_quit) self.connect('destroy', gtk.main_quit)
self.set_default_size(800, 600) self.set_default_size(800, 600)
self.web_view = webkit.WebView() self.web_view = webkit.WebView()
sw = gtk.ScrolledWindow() sw = gtk.ScrolledWindow()
sw.add(self.web_view) sw.add(self.web_view)
self.add(sw) self.add(sw)
self.show_all() self.show_all()

View file

@ -62,6 +62,7 @@ class BackendStorage(object):
if self.storage: if self.storage:
return self.storage.save('backends', self.name) return self.storage.save('backends', self.name)
class BackendConfig(ValuesDict): class BackendConfig(ValuesDict):
modname = None modname = None
instname = None instname = None
@ -118,6 +119,7 @@ class BackendConfig(ValuesDict):
self.weboob.backends_config.add_backend(self.instname, self.modname, dump, edit) self.weboob.backends_config.add_backend(self.instname, self.modname, dump, edit)
class BaseBackend(object): class BaseBackend(object):
# Backend name. # Backend name.
NAME = None NAME = None
@ -149,7 +151,8 @@ class BaseBackend(object):
# NOT yet filled. # NOT yet filled.
OBJECTS = {} OBJECTS = {}
class ConfigError(Exception): pass class ConfigError(Exception):
pass
def __enter__(self): def __enter__(self):
self.lock.acquire() self.lock.acquire()
@ -184,6 +187,7 @@ class BaseBackend(object):
class classprop(object): class classprop(object):
def __init__(self, fget): def __init__(self, fget):
self.fget = fget self.fget = fget
def __get__(self, inst, objtype=None): def __get__(self, inst, objtype=None):
if inst: if inst:
return self.fget(inst) return self.fget(inst)

View file

@ -1 +0,0 @@

View file

@ -28,6 +28,7 @@ from weboob.tools.test import BackendTest
__all__ = ['GenericComicReaderBackend'] __all__ = ['GenericComicReaderBackend']
class DisplayPage(BasePage): class DisplayPage(BasePage):
def get_page(self, gallery): def get_page(self, gallery):
src = self.document.xpath(self.browser.params['img_src_xpath'])[0] src = self.document.xpath(self.browser.params['img_src_xpath'])[0]
@ -102,7 +103,6 @@ class GenericComicReaderBackend(BaseBackend, ICapGallery):
else: else:
return None return None
gallery = BaseGallery(_id, url=(self.ID_TO_URL % _id)) gallery = BaseGallery(_id, url=(self.ID_TO_URL % _id))
with self.browser: with self.browser:
return gallery return gallery
@ -116,7 +116,7 @@ class GenericComicReaderBackend(BaseBackend, ICapGallery):
OBJECTS = { OBJECTS = {
BaseGallery: fill_gallery, BaseGallery: fill_gallery,
BaseImage: fill_image } BaseImage: fill_image}
class GenericComicReaderTest(BackendTest): class GenericComicReaderTest(BackendTest):
@ -126,4 +126,3 @@ class GenericComicReaderTest(BackendTest):
it.next() it.next()
img = it.next() img = it.next()
self.backend.fillobj(img, ('url', 'data')) self.backend.fillobj(img, ('url', 'data'))

View file

@ -20,11 +20,12 @@
# python2.5 compatibility # python2.5 compatibility
from __future__ import with_statement from __future__ import with_statement
import time import time
from weboob.capabilities.messages import ICapMessages, Message, Thread from weboob.capabilities.messages import ICapMessages, Message, Thread
from weboob.tools.backend import BaseBackend from weboob.tools.backend import BaseBackend
from weboob.tools.newsfeed import Newsfeed from weboob.tools.newsfeed import Newsfeed
class GenericNewspaperBackend(BaseBackend, ICapMessages): class GenericNewspaperBackend(BaseBackend, ICapMessages):
"GenericNewspaperBackend class" "GenericNewspaperBackend class"
MAINTAINER = 'Julien Hebert' MAINTAINER = 'Julien Hebert'
@ -71,13 +72,13 @@ class GenericNewspaperBackend(BaseBackend, ICapMessages):
content=content.body, content=content.body,
signature='URL: %s' % content.url, signature='URL: %s' % content.url,
flags=flags, flags=flags,
children= []) children=[])
return thread return thread
def iter_threads(self): def iter_threads(self):
for article in Newsfeed(self.RSS_FEED, GenericNewspaperBackend.RSSID).iter_entries(): for article in Newsfeed(self.RSS_FEED, GenericNewspaperBackend.RSSID).iter_entries():
thread = Thread(article.id) thread = Thread(article.id)
thread.title = article.title thread.title = article.title
thread.date = article.datetime thread.date = article.datetime
yield(thread) yield(thread)
@ -112,7 +113,7 @@ class GenericNewspaperBackend(BaseBackend, ICapMessages):
if time.time() - lastpurge > 7200: if time.time() - lastpurge > 7200:
self.storage.set('lastpurge', time.time()) self.storage.set('lastpurge', time.time())
for id in self.storage.get('seen', default={}): for id in self.storage.get('seen', default={}):
l.append((int(url2id(id)), id)) l.append((int(url2id(id)), id))
l.sort() l.sort()
l.reverse() l.reverse()
tosave = [v[1] for v in l[0:self.RSSSIZE + 10]] tosave = [v[1] for v in l[0:self.RSSSIZE + 10]]

View file

@ -23,8 +23,8 @@ from lxml.etree import Comment
def try_remove(parser, base_element, selector): def try_remove(parser, base_element, selector):
try : try:
base_element.remove(parser.select(base_element, selector, 1 )) base_element.remove(parser.select(base_element, selector, 1))
except (BrokenPageError, ValueError): except (BrokenPageError, ValueError):
pass pass
@ -33,6 +33,7 @@ def try_drop_tree(parser, base_element, selector):
for el in parser.select(base_element, selector): for el in parser.select(base_element, selector):
el.drop_tree() el.drop_tree()
def remove_from_selector_list(parser, base_element, selector_list): def remove_from_selector_list(parser, base_element, selector_list):
for selector in selector_list: for selector in selector_list:
base_element.remove(parser.select(base_element, selector, 1)) base_element.remove(parser.select(base_element, selector, 1))
@ -42,24 +43,28 @@ def try_remove_from_selector_list(parser, base_element, selector_list):
for selector in selector_list: for selector in selector_list:
try_remove(parser, base_element, selector) try_remove(parser, base_element, selector)
def drop_comments(base_element): def drop_comments(base_element):
for comment in base_element.getiterator(Comment): for comment in base_element.getiterator(Comment):
comment.drop_tree() comment.drop_tree()
class NoAuthorElement(BrokenPageError): class NoAuthorElement(BrokenPageError):
pass pass
class NoBodyElement(BrokenPageError): class NoBodyElement(BrokenPageError):
pass pass
class NoTitleException(BrokenPageError): class NoTitleException(BrokenPageError):
pass pass
class NoneMainDiv(AttributeError): class NoneMainDiv(AttributeError):
pass pass
class Article(object): class Article(object):
author = u'' author = u''
title = u'' title = u''
@ -71,10 +76,11 @@ class Article(object):
self.url = u'' self.url = u''
self.date = None self.date = None
class GenericNewsPage(BasePage): class GenericNewsPage(BasePage):
__element_body = NotImplementedError __element_body = NotImplementedError
__article = Article __article = Article
element_title_selector = NotImplementedError element_title_selector = NotImplementedError
main_div = NotImplementedError main_div = NotImplementedError
element_body_selector = NotImplementedError element_body_selector = NotImplementedError
element_author_selector = NotImplementedError element_author_selector = NotImplementedError
@ -90,7 +96,7 @@ class GenericNewsPage(BasePage):
return self.__article.author return self.__article.author
def get_title(self): def get_title(self):
try : try:
return self.parser.select( return self.parser.select(
self.main_div, self.main_div,
self.element_title_selector, self.element_title_selector,
@ -108,7 +114,7 @@ class GenericNewsPage(BasePage):
return self.get_title() return self.get_title()
def get_element_body(self): def get_element_body(self):
try : try:
return self.parser.select(self.main_div, self.element_body_selector, 1) return self.parser.select(self.main_div, self.element_body_selector, 1)
except BrokenPageError: except BrokenPageError:
raise NoBodyElement("no body on %s" % (self.browser)) raise NoBodyElement("no body on %s" % (self.browser))

View file

@ -21,87 +21,88 @@
import hashlib import hashlib
import Image import Image
class VirtKeyboardError(Exception): class VirtKeyboardError(Exception):
def __init__(self, msg): def __init__(self, msg):
Exception.__init__(self, msg) Exception.__init__(self, msg)
class VirtKeyboard(object): class VirtKeyboard(object):
def __init__(self, file,coords,color): def __init__(self, file, coords, color):
# file: virtual keyboard image # file: virtual keyboard image
# coords: dictionary <value to return>:<tuple(x1,y1,x2,y2)> # coords: dictionary <value to return>:<tuple(x1,y1,x2,y2)>
# color: color of the symbols in the image # color: color of the symbols in the image
# depending on the image, it can be a single value or a tuple # depending on the image, it can be a single value or a tuple
img=Image.open(file) img = Image.open(file)
self.bands=img.getbands() self.bands = img.getbands()
if isinstance(color,int) and not isinstance(self.bands,str) and len(self.bands)!=1: if isinstance(color, int) and not isinstance(self.bands, str) and len(self.bands) != 1:
raise VirtKeyboardError("Color requires %i component but only 1 is provided" \ raise VirtKeyboardError("Color requires %i component but only 1 is provided" \
% len(self.bands)) % len(self.bands))
if not isinstance(color, int) and len(color)!=len(self.bands): if not isinstance(color, int) and len(color) != len(self.bands):
raise VirtKeyboardError("Color requires %i components but %i are provided" \ raise VirtKeyboardError("Color requires %i components but %i are provided" \
% (len(self.bands),len(color))) % (len(self.bands), len(color)))
self.color=color self.color = color
(self.width,self.height)=img.size (self.width, self.height) = img.size
self.pixar=img.load() self.pixar = img.load()
self.coords={} self.coords = {}
self.md5={} self.md5 = {}
for i in coords.keys(): for i in coords.keys():
coord=self.get_symbol_coords(coords[i]) coord = self.get_symbol_coords(coords[i])
if coord==(-1,-1,-1,-1): if coord == (-1, -1, -1, -1):
continue continue
self.coords[i]=coord self.coords[i] = coord
self.md5[i]=self.checksum(self.coords[i]) self.md5[i] = self.checksum(self.coords[i])
def get_symbol_coords(self,(x1,y1,x2,y2)): def get_symbol_coords(self, (x1, y1, x2, y2)):
newY1=-1 newY1 = -1
newY2=-1 newY2 = -1
for y in range(y1,min(y2+1,self.height)): for y in range(y1, min(y2 + 1, self.height)):
empty_line=True empty_line = True
for x in range(x1,min(x2+1,self.width)): for x in range(x1, min(x2 + 1, self.width)):
if self.pixar[x,y] == self.color: if self.pixar[x, y] == self.color:
empty_line=False empty_line = False
if newY1==-1: if newY1 == -1:
newY1=y newY1 = y
break;
else:
break
if newY1!=-1 and not empty_line:
newY2=y
newX1=-1
newX2=-1
for x in range(x1,min(x2+1,self.width)):
empty_column=True
for y in range(y1,min(y2+1,self.height)):
if self.pixar[x,y] == self.color:
empty_column=False
if newX1==-1:
newX1=x
break break
else: else:
break break
if newX1!=-1 and not empty_column: if newY1 != -1 and not empty_line:
newX2=x newY2 = y
return (newX1,newY1,newX2,newY2) newX1 = -1
newX2 = -1
for x in range(x1, min(x2 + 1, self.width)):
empty_column = True
for y in range(y1, min(y2 + 1, self.height)):
if self.pixar[x, y] == self.color:
empty_column = False
if newX1 == -1:
newX1 = x
break
else:
break
if newX1 != -1 and not empty_column:
newX2 = x
return (newX1, newY1, newX2, newY2)
def checksum(self,(x1,y1,x2,y2)): def checksum(self, (x1, y1, x2, y2)):
s = '' s = ''
for y in range(y1,min(y2+1,self.height)): for y in range(y1, min(y2 + 1, self.height)):
for x in range(x1,min(x2+1,self.width)): for x in range(x1, min(x2 + 1, self.width)):
if self.pixar[x,y]==self.color: if self.pixar[x, y] == self.color:
s += "." s += "."
else: else:
s += " " s += " "
return hashlib.md5(s).hexdigest() return hashlib.md5(s).hexdigest()
def get_symbol_code(self,md5sum): def get_symbol_code(self, md5sum):
for i in self.md5.keys(): for i in self.md5.keys():
if md5sum == self.md5[i]: if md5sum == self.md5[i]:
return i return i
raise VirtKeyboardError('Symbol not found') raise VirtKeyboardError('Symbol not found')
def check_symbols(self,symbols,dirname): def check_symbols(self, symbols, dirname):
# symbols: dictionary <symbol>:<md5 value> # symbols: dictionary <symbol>:<md5 value>
for s in symbols.keys(): for s in symbols.keys():
try: try:
@ -109,32 +110,33 @@ class VirtKeyboard(object):
except VirtKeyboardError: except VirtKeyboardError:
self.generate_MD5(dirname) self.generate_MD5(dirname)
raise VirtKeyboardError("Symbol '%s' not found; all symbol hashes are available in %s"\ raise VirtKeyboardError("Symbol '%s' not found; all symbol hashes are available in %s"\
% (s,dirname)) % (s, dirname))
def generate_MD5(self,dir): def generate_MD5(self, dir):
for i in self.coords.keys(): for i in self.coords.keys():
width=self.coords[i][2]-self.coords[i][0]+1 width = self.coords[i][2] - self.coords[i][0] + 1
height=self.coords[i][3]-self.coords[i][1]+1 height = self.coords[i][3] - self.coords[i][1] + 1
img=Image.new(''.join(self.bands),(width,height)) img = Image.new(''.join(self.bands), (width, height))
matrix=img.load() matrix = img.load()
for y in range(height): for y in range(height):
for x in range(width): for x in range(width):
matrix[x,y]=self.pixar[self.coords[i][0]+x,self.coords[i][1]+y] matrix[x, y] = self.pixar[self.coords[i][0] + x, self.coords[i][1] + y]
img.save(dir+"/"+self.md5[i]+".png") img.save(dir + "/" + self.md5[i] + ".png")
class MappedVirtKeyboard(VirtKeyboard): class MappedVirtKeyboard(VirtKeyboard):
def __init__(self, file, document, img_element, color, map_attr="onclick"): def __init__(self, file, document, img_element, color, map_attr="onclick"):
map_id=img_element.attrib.get("usemap")[1:] map_id = img_element.attrib.get("usemap")[1:]
map=document.find("//map[@id='"+map_id+"']") map = document.find("//map[@id='" + map_id + "']")
if map is None: if map is None:
map=document.find("//map[@name='"+map_id+"']") map = document.find("//map[@name='" + map_id + "']")
coords={} coords = {}
for area in map.getiterator("area"): for area in map.getiterator("area"):
code=area.attrib.get(map_attr) code = area.attrib.get(map_attr)
area_coords=[] area_coords = []
for coord in area.attrib.get("coords").split(','): for coord in area.attrib.get("coords").split(','):
area_coords.append(int(coord)) area_coords.append(int(coord))
coords[code]=tuple(area_coords) coords[code] = tuple(area_coords)
VirtKeyboard.__init__(self,file,coords,color) VirtKeyboard.__init__(self, file, coords, color)

View file

@ -22,7 +22,7 @@ class ConfigError(Exception):
pass pass
class IConfig: class IConfig(object):
def load(self, default={}): def load(self, default={}):
raise NotImplementedError() raise NotImplementedError()

View file

@ -40,13 +40,12 @@ def retry(ExceptionToCheck, tries=4, delay=3, backoff=2):
try_one_last_time = False try_one_last_time = False
break break
except ExceptionToCheck, e: except ExceptionToCheck, e:
logging.debug(u'%s, Retrying in %d seconds...' % (e, logging.debug(u'%s, Retrying in %d seconds...' % (e, mdelay))
mdelay))
time.sleep(mdelay) time.sleep(mdelay)
mtries -= 1 mtries -= 1
mdelay *= backoff mdelay *= backoff
if try_one_last_time: if try_one_last_time:
return f(*args, **kwargs) return f(*args, **kwargs)
return return
return f_retry # true decorator return f_retry # true decorator
return deco_retry return deco_retry

View file

@ -35,11 +35,13 @@ COLORS = {
'CRITICAL': COLOR_SEQ % ("\033[1;33m\033[1;41m"), 'CRITICAL': COLOR_SEQ % ("\033[1;33m\033[1;41m"),
} }
def getLogger(name, parent=None): def getLogger(name, parent=None):
if parent: if parent:
name = parent.name + '.' + name name = parent.name + '.' + name
return _getLogger(name) return _getLogger(name)
class ColoredFormatter(Formatter): class ColoredFormatter(Formatter):
""" """
Class written by airmind: Class written by airmind:
@ -52,9 +54,9 @@ class ColoredFormatter(Formatter):
msg = COLORS[levelname] % msg msg = COLORS[levelname] % msg
return msg return msg
def createColoredFormatter(stream, format): def createColoredFormatter(stream, format):
if (sys.platform != 'win32') and stream.isatty(): if (sys.platform != 'win32') and stream.isatty():
return ColoredFormatter(format) return ColoredFormatter(format)
else: else:
return Formatter(format) return Formatter(format)

View file

@ -68,6 +68,7 @@ try:
html2text = h2t.html2text html2text = h2t.html2text
except ImportError: except ImportError:
warning('python-html2text is not present. HTML pages will not be converted into text.') warning('python-html2text is not present. HTML pages will not be converted into text.')
def html2text(html): def html2text(html):
return html return html
@ -117,6 +118,7 @@ def utc2local(date):
date = date.astimezone(tz.tzlocal()) date = date.astimezone(tz.tzlocal())
return date return date
def limit(iterator, lim): def limit(iterator, lim):
count = 0 count = 0
iterator = iter(iterator) iterator = iter(iterator)
@ -125,6 +127,7 @@ def limit(iterator, lim):
count += 1 count += 1
raise StopIteration() raise StopIteration()
def ratelimit(group, delay): def ratelimit(group, delay):
""" """
Simple rate limiting. Simple rate limiting.

View file

@ -29,46 +29,46 @@ if feedparser.__version__ >= '5.0':
__all__ = ['Entry', 'Newsfeed'] __all__ = ['Entry', 'Newsfeed']
class Entry: class Entry(object):
def __init__(self, entry, rssid_func=None): def __init__(self, entry, rssid_func=None):
if hasattr(entry, 'id'): if hasattr(entry, 'id'):
self.id = entry.id self.id = entry.id
else: else:
self.id = None self.id = None
if entry.has_key("link"): if "link" in entry:
self.link = entry["link"] self.link = entry["link"]
else: else:
self.link = None self.link = None
if entry.has_key("title"): if "title" in entry:
self.title = entry["title"] self.title = entry["title"]
else: else:
self.title = None self.title = None
if entry.has_key("author"): if "author" in entry:
self.author = entry["author"] self.author = entry["author"]
else: else:
self.author = None self.author = None
if entry.has_key("updated_parsed"): if "updated_parsed" in entry:
self.datetime = datetime.datetime(*entry['updated_parsed'][:7]) self.datetime = datetime.datetime(*entry['updated_parsed'][:7])
else: else:
self.datetime = None self.datetime = None
if entry.has_key("summary"): if "summary" in entry:
self.summary = entry["summary"] self.summary = entry["summary"]
else: else:
self.summary = None self.summary = None
self.content = [] self.content = []
if entry.has_key("content"): if "content" in entry:
for i in entry["content"]: for i in entry["content"]:
self.content.append(i.value) self.content.append(i.value)
elif self.summary: elif self.summary:
self.content.append(self.summary) self.content.append(self.summary)
if entry.has_key("wfw_commentrss"): if "wfw_commentrss" in entry:
self.rsscomment = entry["wfw_commentrss"] self.rsscomment = entry["wfw_commentrss"]
else: else:
self.rsscomment = None self.rsscomment = None
@ -76,7 +76,8 @@ class Entry:
if rssid_func: if rssid_func:
self.id = rssid_func(self) self.id = rssid_func(self)
class Newsfeed:
class Newsfeed(object):
def __init__(self, url, rssid_func=None): def __init__(self, url, rssid_func=None):
self.feed = feedparser.parse(url) self.feed = feedparser.parse(url)
self.rssid_func = rssid_func self.rssid_func = rssid_func

View file

@ -30,7 +30,6 @@ except ImportError:
## {{{ http://code.activestate.com/recipes/576693/ (r6) ## {{{ http://code.activestate.com/recipes/576693/ (r6)
from UserDict import DictMixin from UserDict import DictMixin
class OrderedDict(dict, DictMixin): class OrderedDict(dict, DictMixin):
def __init__(self, *args, **kwds): def __init__(self, *args, **kwds):

View file

@ -24,34 +24,42 @@ import logging
__all__ = ['get_parser', 'NoParserFound'] __all__ = ['get_parser', 'NoParserFound']
class NoParserFound(Exception): pass class NoParserFound(Exception):
pass
def load_lxml(): def load_lxml():
from .lxmlparser import LxmlHtmlParser from .lxmlparser import LxmlHtmlParser
return LxmlHtmlParser return LxmlHtmlParser
def load_lxmlsoup(): def load_lxmlsoup():
from .lxmlsoupparser import LxmlSoupParser from .lxmlsoupparser import LxmlSoupParser
return LxmlSoupParser return LxmlSoupParser
def load_html5lib(): def load_html5lib():
from .html5libparser import Html5libParser from .html5libparser import Html5libParser
return Html5libParser return Html5libParser
def load_elementtidy(): def load_elementtidy():
from .elementtidyparser import ElementTidyParser from .elementtidyparser import ElementTidyParser
return ElementTidyParser return ElementTidyParser
def load_builtin(): def load_builtin():
from .htmlparser import HTMLParser from .htmlparser import HTMLParser
return HTMLParser return HTMLParser
def load_json(): def load_json():
# This parser doesn't read HTML, don't include it in the # This parser doesn't read HTML, don't include it in the
# preference_order default value below. # preference_order default value below.
from .jsonparser import JsonParser from .jsonparser import JsonParser
return JsonParser return JsonParser
def get_parser(preference_order=('lxml', 'lxmlsoup')): def get_parser(preference_order=('lxml', 'lxmlsoup')):
""" """
Get a parser from a preference order list. Get a parser from a preference order list.

View file

@ -38,8 +38,8 @@ class Html5libParser(HTMLParser, IParser):
""" """
# Default implementation for each type of API. # Default implementation for each type of API.
defaults = {'etree': ElementTree, defaults = {'etree': ElementTree}
}
def __init__(self, api='etree'): def __init__(self, api='etree'):
# if no default implementation is defined for this api, set it to None # if no default implementation is defined for this api, set it to None
# to let getTreeBuilder() using the corresponding implementation. # to let getTreeBuilder() using the corresponding implementation.

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# For Python 2.5-, this will enable the simliar property mechanism as in # For Python 2.5-, this will enable the similar property mechanism as in
# Python 2.6+/3.0+. The code is based on # Python 2.6+/3.0+. The code is based on
# http://bruynooghe.blogspot.com/2008/04/xsetter-syntax-in-python-25.html # http://bruynooghe.blogspot.com/2008/04/xsetter-syntax-in-python-25.html
@ -27,26 +27,26 @@ import __builtin__
class property(property): class property(property):
def __init__(self, fget, *args, **kwargs): def __init__(self, fget, *args, **kwargs):
self.__doc__ = fget.__doc__ self.__doc__ = fget.__doc__
super(property, self).__init__(fget, *args, **kwargs) super(property, self).__init__(fget, *args, **kwargs)
def setter(self, fset): def setter(self, fset):
cls_ns = sys._getframe(1).f_locals cls_ns = sys._getframe(1).f_locals
for k, v in cls_ns.iteritems(): for k, v in cls_ns.iteritems():
if v == self: if v == self:
propname = k propname = k
break break
cls_ns[propname] = property(self.fget, fset, self.fdel, self.__doc__) cls_ns[propname] = property(self.fget, fset, self.fdel, self.__doc__)
return cls_ns[propname] return cls_ns[propname]
def deleter(self, fdel): def deleter(self, fdel):
cls_ns = sys._getframe(1).f_locals cls_ns = sys._getframe(1).f_locals
for k, v in cls_ns.iteritems(): for k, v in cls_ns.iteritems():
if v == self: if v == self:
propname = k propname = k
break break
cls_ns[propname] = property(self.fget, self.fset, fdel, self.__doc__) cls_ns[propname] = property(self.fget, self.fset, fdel, self.__doc__)
return cls_ns[propname] return cls_ns[propname]
__builtin__.property = property __builtin__.property = property

View file

@ -23,7 +23,7 @@ from copy import deepcopy
from .config.yamlconfig import YamlConfig from .config.yamlconfig import YamlConfig
class IStorage: class IStorage(object):
def load(self, what, name, default={}): def load(self, what, name, default={}):
raise NotImplementedError() raise NotImplementedError()