diff --git a/modules/arte/pages.py b/modules/arte/pages.py
index 816371cce5d9c12ab913dfd89cdfa712bd129b38..d41600785b935c240bda14c1396b3b92492e0ef8 100644
--- a/modules/arte/pages.py
+++ b/modules/arte/pages.py
@@ -29,6 +29,7 @@ from weboob.browser.elements import DictElement, ItemElement, ListElement, metho
from weboob.browser.filters.standard import Date, Env, CleanText, Field, ItemNotFound, BrowserURL
from weboob.browser.filters.json import Dict
from weboob.tools.date import parse_french_date
+from weboob.tools.compat import basestring
class ArteItemElement(ItemElement):
diff --git a/modules/arte/test.py b/modules/arte/test.py
index 73ac5a618775fc0964a3da237e0c46819b885fa9..408c94415cce046be78515c27fa6a8a292be138d 100644
--- a/modules/arte/test.py
+++ b/modules/arte/test.py
@@ -17,6 +17,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see .
+import itertools
from weboob.tools.test import BackendTest
from weboob.tools.value import Value
@@ -45,11 +46,11 @@ class ArteTest(BackendTest):
def test_sites(self):
for site in SITE.values:
- l1 = list(self.backend.iter_resources([BaseVideo], [site.get('id')]))
+ l1 = list(itertools.islice(self.backend.iter_resources([BaseVideo], [site.get('id')]), 0, 20))
assert len(l1)
while not isinstance(l1[0], BaseVideo):
- l1 = list(self.backend.iter_resources([BaseVideo], l1[-1].split_path))
+ l1 = list(itertools.islice(self.backend.iter_resources([BaseVideo], l1[-1].split_path), 0, 20))
assert len(l1)
for v in l1:
diff --git a/modules/aum/browser.py b/modules/aum/browser.py
index 7d96a78b76e81080351bef9a32aed62968c1fb48..a3f1c1b02876dfdb4e5c29201bfa033b5b24e7c4 100644
--- a/modules/aum/browser.py
+++ b/modules/aum/browser.py
@@ -154,9 +154,9 @@ class AuMBrowser(DomainBrowser):
headers = kwargs.setdefault('headers', {})
if 'applications' not in url:
today = local2utc(datetime.now()).strftime('%Y-%m-%d')
- token = sha256(self.username + self.APITOKEN + today).hexdigest()
+ token = sha256((self.username + self.APITOKEN + today).encode('utf-8')).hexdigest()
- headers['Authorization'] = 'Basic %s' % (b64encode('%s:%s' % (self.username, self.password)))
+ headers['Authorization'] = 'Basic %s' % (b64encode(b'%s:%s' % (self.username.encode('utf-8'), self.password.encode('utf-8')))).decode('utf-8')
headers['X-Platform'] = 'android'
headers['X-Client-Version'] = self.APIVERSION
headers['X-AUM-Token'] = token
@@ -181,8 +181,8 @@ class AuMBrowser(DomainBrowser):
return self.consts
self.consts = [{}, {}]
- for key, sexes in self.request('values').iteritems():
- for sex, values in sexes.iteritems():
+ for key, sexes in self.request('values').items():
+ for sex, values in sexes.items():
if sex in ('boy', 'both'):
self.consts[0][key] = values
if sex in ('girl', 'both'):
diff --git a/modules/aum/contact.py b/modules/aum/contact.py
index 0dbd56d66402675a7dd90220f0869742cf5b0623..98db46484b7d2030140461e956558c7a5e84ee7a 100644
--- a/modules/aum/contact.py
+++ b/modules/aum/contact.py
@@ -26,6 +26,7 @@ from collections import OrderedDict
from weboob.capabilities.contact import Contact as _Contact, ProfileNode
from weboob.tools.html import html2text
+from weboob.tools.compat import unicode, basestring
class FieldBase(object):
@@ -242,7 +243,7 @@ class Contact(_Contact):
self.profile = OrderedDict()
if 'sex' in profile:
- for section, d in self.TABLE.iteritems():
+ for section, d in self.TABLE.items():
flags = ProfileNode.SECTION
if section.startswith('_'):
flags |= ProfileNode.HEAD
@@ -254,7 +255,7 @@ class Contact(_Contact):
s = ProfileNode(section, section.capitalize(), OrderedDict(), flags=flags)
- for key, builder in d.iteritems():
+ for key, builder in d.items():
try:
value = builder.get_value(profile, consts[int(profile['sex'])])
except KeyError:
diff --git a/modules/aum/module.py b/modules/aum/module.py
index 195dc6756a694ad0340e8ba6db26c878f6610116..b94ac941a32517d52ca7e54562e6923f09c92bd5 100644
--- a/modules/aum/module.py
+++ b/modules/aum/module.py
@@ -36,6 +36,7 @@ from weboob.exceptions import BrowserUnavailable, BrowserHTTPNotFound
from weboob.tools.value import Value, ValueBool, ValueBackendPassword
from weboob.tools.date import local2utc
from weboob.tools.misc import to_unicode
+from weboob.tools.compat import unicode, long, basestring
from .contact import Contact
from .antispam import AntiSpam
@@ -104,7 +105,7 @@ class AuMModule(Module, CapMessages, CapMessagesPost, CapDating, CapChat, CapCon
all_events[u'baskets'] = (self.browser.get_baskets, 'You were put into %s\'s basket')
all_events[u'flashs'] = (self.browser.get_flashs, 'You sent a charm to %s')
all_events[u'visits'] = (self.browser.get_visits, 'Visited by %s')
- for type, (events, message) in all_events.iteritems():
+ for type, (events, message) in all_events.items():
for event in events():
e = Event(event['who']['id'])
@@ -343,7 +344,7 @@ class AuMModule(Module, CapMessages, CapMessagesPost, CapDating, CapChat, CapCon
if 'profile' in fields:
contact = self.get_contact(contact)
if contact and 'photos' in fields:
- for name, photo in contact.photos.iteritems():
+ for name, photo in contact.photos.items():
if photo.url and not photo.data:
data = self.browser.openurl(photo.url).read()
contact.set_photo(name, data=data)
diff --git a/modules/bibliothequesparis/browser.py b/modules/bibliothequesparis/browser.py
index 65b86c4b671ce8dd8c018f5242dc0e2e84caa656..a4d750d6acc0187ffd3d6e2e8a5c5f0f255f5dac 100644
--- a/modules/bibliothequesparis/browser.py
+++ b/modules/bibliothequesparis/browser.py
@@ -22,7 +22,7 @@ from __future__ import unicode_literals
from weboob.browser import LoginBrowser, URL, need_login
from weboob.capabilities.base import find_object
-from .pages import LoginPage, LoansPage, RenewPage
+from .pages import LoginPage, LoansPage, RenewPage, SearchPage
class BibliothequesparisBrowser(LoginBrowser):
@@ -31,6 +31,7 @@ class BibliothequesparisBrowser(LoginBrowser):
login = URL(r'/Default/Portal/Recherche/logon.svc/logon', LoginPage)
bookings = URL('/Default/Portal/Recherche/Search.svc/RenderAccountWebFrame', LoansPage)
renew = URL(r'/Default/Portal/Services/ILSClient.svc/RenewLoans', RenewPage)
+ search = URL(r'/Default/Portal/Recherche/Search.svc/Search', SearchPage)
json_headers = {
'Accept': 'application/json, text/javascript',
@@ -59,3 +60,26 @@ class BibliothequesparisBrowser(LoginBrowser):
assert b._renew_data, 'book has no data'
post = u'{"loans":[%s]}' % b._renew_data
self.renew.go(data=post.encode('utf-8'), headers=self.json_headers)
+
+ def search_books(self, pattern):
+ max_page = 0
+ page = 0
+ while page <= max_page:
+ d = {
+ "query": {
+ "Page": page,
+ "PageRange": 3,
+ "QueryString": pattern,
+ "ResultSize": 50,
+ "ScenarioCode": "CATALOGUE",
+ "SearchContext": 0,
+ "SearchLabel": "",
+ "Url": "https://bibliotheques.paris.fr/Default/search.aspx?SC=CATALOGUE&QUERY={q}&QUERY_LABEL=#/Search/(query:(Page:{page},PageRange:3,QueryString:{q},ResultSize:50,ScenarioCode:CATALOGUE,SearchContext:0,SearchLabel:''))".format(q=pattern, page=page),
+ }
+ }
+ self.location('/Default/Portal/Recherche/Search.svc/Search', json=d, headers=self.json_headers)
+ for book in self.page.iter_books():
+ yield book
+
+ max_page = self.page.get_max_page()
+ page += 1
diff --git a/modules/bibliothequesparis/module.py b/modules/bibliothequesparis/module.py
index ff370b31869b1a37b198910fbe18c785bf558dd9..646a4edd4239e00dd2cb4d5bf607ad9fea884fe0 100644
--- a/modules/bibliothequesparis/module.py
+++ b/modules/bibliothequesparis/module.py
@@ -62,5 +62,5 @@ class BibliothequesparisModule(Module, CapBook):
def renew_book(self, _id):
return self.browser.do_renew(_id)
- def search_books(self, _string):
- raise NotImplementedError()
+ def search_books(self, pattern):
+ return self.browser.search_books(pattern)
diff --git a/modules/bibliothequesparis/pages.py b/modules/bibliothequesparis/pages.py
index 9a57c33e6827612929ee85d4c0dd7183e2777781..920e8044b74cb9caebde52214394a7da847b5bcd 100644
--- a/modules/bibliothequesparis/pages.py
+++ b/modules/bibliothequesparis/pages.py
@@ -20,9 +20,10 @@
from __future__ import unicode_literals
from weboob.browser.pages import HTMLPage, JsonPage, LoggedPage
-from weboob.browser.elements import ListElement, ItemElement, method
+from weboob.browser.elements import ListElement, ItemElement, method, DictElement
from weboob.browser.filters.standard import CleanText, Date, Regexp, Field
from weboob.browser.filters.html import Link
+from weboob.browser.filters.json import Dict
from weboob.capabilities.base import UserError
from weboob.capabilities.library import Book
@@ -82,3 +83,20 @@ class LoansPage(LoggedPage, JsonMixin):
class RenewPage(LoggedPage, JsonMixin):
pass
+
+
+class SearchPage(LoggedPage, JsonPage):
+ @method
+ class iter_books(DictElement):
+ item_xpath = 'd/Results'
+
+ class item(ItemElement):
+ klass = Book
+
+ obj_url = Dict('FriendlyUrl')
+ obj_id = Dict('Resource/RscId')
+ obj_name = Dict('Resource/Ttl')
+ obj_author = Dict('Resource/Crtr', default=None)
+
+ def get_max_page(self):
+ return self.doc['d']['SearchInfo']['PageMax']
diff --git a/modules/github/browser.py b/modules/github/browser.py
index 87658cfccf64d92333c232e10f922de4d883986c..8dbce0392d11224487aa661b824b67d997ec5501 100644
--- a/modules/github/browser.py
+++ b/modules/github/browser.py
@@ -17,15 +17,16 @@
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see .
+from __future__ import unicode_literals
-import datetime
import re
import os
+from dateutil.parser import parse as parse_date
+from weboob.capabilities.base import empty
from weboob.browser.browsers import APIBrowser
from weboob.browser.cache import CacheMixin
from weboob.browser.exceptions import ClientError
-from weboob.tools.compat import quote_plus
__all__ = ['GithubBrowser']
@@ -46,6 +47,11 @@ class GithubBrowser(CacheMixin, APIBrowser):
'id': project_id
}
+ def iter_labels(self, project_id):
+ json = self.request('https://api.github.com/repos/%s/labels' % project_id)
+ for d in json:
+ yield d['name']
+
def get_issue(self, project_id, issue_number):
json = self.request('https://api.github.com/repos/%s/issues/%s' % (project_id, issue_number))
return self._make_issue(project_id, issue_number, json)
@@ -60,7 +66,12 @@ class GithubBrowser(CacheMixin, APIBrowser):
break
def iter_issues(self, query):
- qsparts = ['repo:%s' % query.project]
+ def escape(s):
+ if ' ' in s:
+ return '"%s"' % s
+ return s
+
+ qsparts = ['repo:%s' % query.project.id]
if query.assignee:
qsparts.append('assignee:%s' % query.assignee)
if query.author:
@@ -69,14 +80,16 @@ class GithubBrowser(CacheMixin, APIBrowser):
qsparts.append('state:%s' % query.status)
if query.title:
qsparts.append('%s in:title' % query.title)
+ if query.tags:
+ qsparts.append(' '.join('label:%s' % escape(tag) for tag in query.tags))
- qs = quote_plus(' '.join(qsparts))
+ qs = ' '.join(qsparts)
- base_url = 'https://api.github.com/search/issues?q=%s' % qs
- for json in self._paginated(base_url):
+ base_url = 'https://api.github.com/search/issues'
+ for json in self._paginated(base_url, params={'q': qs}):
for jissue in json['items']:
issue_number = jissue['number']
- yield self._make_issue(query.project, issue_number, jissue)
+ yield self._make_issue(query.project.id, issue_number, jissue)
if not len(json['items']):
break
@@ -105,6 +118,8 @@ class GithubBrowser(CacheMixin, APIBrowser):
data['milestone'] = issue.version.id
if issue.status:
data['state'] = issue.status.name # TODO improve if more statuses are implemented
+ if not empty(issue.tags):
+ data['labels'] = [tag.name for tag in issue.tags]
return data
def post_comment(self, issue_id, comment):
@@ -135,6 +150,7 @@ class GithubBrowser(CacheMixin, APIBrowser):
d['version'] = None
d['has_comments'] = (json['comments'] > 0)
d['attachments'] = list(self._extract_attachments(d['body']))
+ d['labels'] = json['labels']
# TODO fetch other updates?
return d
@@ -161,6 +177,48 @@ class GithubBrowser(CacheMixin, APIBrowser):
if len(json) < 100:
break
+ EVENTS = {
+ 'closed': ('state', 'open', 'closed'),
+ 'merged': ('state', 'open', 'closed'),
+ 'reopened': ('state', 'closed', 'open'),
+ 'assigned': ('assignee', None, lambda j: j['assignee']['login']),
+ 'unassigned': ('assignee', lambda j: j['assignee']['login'], None),
+ 'labeled': ('tags', None, lambda j: j['label']['name']),
+ 'unlabeled': ('tags', lambda j: j['label']['name'], None),
+ 'renamed': ('title', lambda j: j['rename']['from'], lambda j: j['rename']['to']),
+ 'locked': ('locked', 'unlocked', 'locked'),
+ 'unlocked': ('locked', 'locked', 'unlocked'),
+ 'milestoned': ('milestone', None, lambda j: j['milestone']['title']),
+ 'demilestoned': ('milestone', lambda j: j['milestone']['title'], None),
+ 'marked_as_duplicate': ('duplicate', 'no', 'yes'), # no link to other issue?
+ 'unmarked_as_duplicate': ('duplicate', 'yes', 'no'),
+ }
+
+ def iter_events(self, project_id, issue_number):
+ url = 'https://api.github.com/repos/%s/issues/%s/events' % (project_id, issue_number)
+ for json in self._paginated(url):
+ for jevent in json:
+ d = {}
+ d['id'] = jevent['id']
+ d['author'] = jevent['actor']['login']
+ d['date'] = parse_date(jevent['created_at'])
+
+ if jevent['event'] not in self.EVENTS:
+ self.logger.info('ignoring event %r', jevent['event'])
+ continue
+ d['field'], old, new = self.EVENTS[jevent['event']]
+ if callable(old):
+ old = old(jevent)
+ if callable(new):
+ new = new(jevent)
+
+ d['old'] = old
+ d['new'] = new
+ yield d
+
+ if len(json) < 100:
+ break
+
def _extract_attachments(self, message):
for attach_url in re.findall(r'https://f.cloud.github.com/assets/[\w/.-]+', message):
yield {
@@ -168,13 +226,13 @@ class GithubBrowser(CacheMixin, APIBrowser):
'filename': os.path.basename(attach_url)
}
- def _paginated(self, url, start_at=1):
+ def _paginated(self, url, start_at=1, params=None):
+ params = (params or {}).copy()
+ params['per_page'] = 100
+
while True:
- if '?' in url:
- page_url = '%s&per_page=100&page=%s' % (url, start_at)
- else:
- page_url = '%s?per_page=100&page=%s' % (url, start_at)
- yield self.request(page_url)
+ params['page'] = start_at
+ yield self.request(url, params=params)
start_at += 1
def get_user(self, _id):
@@ -233,11 +291,4 @@ class GithubBrowser(CacheMixin, APIBrowser):
else:
return {}
-# TODO use a cache for objects and/or pages?
-
-
-def parse_date(s):
- if s.endswith('Z'):
- s = s[:-1]
-
- return datetime.datetime.strptime(s, '%Y-%m-%dT%H:%M:%S')
+# TODO use a cache for objects?
diff --git a/modules/github/module.py b/modules/github/module.py
index 5c36aeb731faa186730499833493dcd25cc564f6..c8ce8a30eda8a89748492fae05d2c13afb9a620e 100644
--- a/modules/github/module.py
+++ b/modules/github/module.py
@@ -20,7 +20,11 @@
from weboob.tools.backend import Module, BackendConfig
from weboob.tools.value import Value, ValueBackendPassword
-from weboob.capabilities.bugtracker import CapBugTracker, Issue, Project, User, Version, Status, Update, Attachment
+from weboob.capabilities.base import empty
+from weboob.capabilities.bugtracker import (
+ CapBugTracker, Issue, Project, User, Version, Status, Update, Attachment,
+ Change,
+)
from .browser import GithubBrowser
@@ -30,7 +34,6 @@ __all__ = ['GithubModule']
STATUSES = {'open': Status('open', u'open', Status.VALUE_NEW),
'closed': Status('closed', u'closed', Status.VALUE_RESOLVED)}
-# TODO tentatively parse github "labels"?
class GithubModule(Module, CapBugTracker):
@@ -41,7 +44,7 @@ class GithubModule(Module, CapBugTracker):
LICENSE = 'AGPLv3+'
VERSION = '1.4'
CONFIG = BackendConfig(Value('username', label='Username', default=''),
- ValueBackendPassword('password', label='Password', default=''))
+ ValueBackendPassword('password', label='Password or Personal token', default=''))
BROWSER = GithubBrowser
@@ -59,8 +62,10 @@ class GithubModule(Module, CapBugTracker):
project = Project(_id, d['name'])
project.members = list(self._iter_members(project.id))
project.statuses = list(STATUSES.values())
+ project.fields = [] # not supported by github
project.categories = []
project.versions = list(self._iter_versions(project.id))
+ project.tags = list(self.browser.iter_labels(project.id))
return project
@@ -73,19 +78,32 @@ class GithubModule(Module, CapBugTracker):
issue = self._make_issue(d, project)
if d['has_comments']:
self._fetch_comments(issue)
+ self._fetch_events(issue)
+ issue.history.sort(key=lambda u: u.date)
return issue
def iter_issues(self, query):
- if ((query.assignee, query.author, query.status, query.title) ==
- (None, None, None, None)):
- it = self.browser.iter_project_issues(query.project)
+ if not query.project:
+ return
+
+ query = query.copy()
+ if query.project and not isinstance(query.project, Project):
+ query.project = self.get_project(query.project)
+ if isinstance(query.status, Status):
+ query.status = query.status.name
+ if isinstance(query.author, User):
+ query.author = query.author.name
+ if isinstance(query.assignee, User):
+ query.assignee = query.assignee.name
+
+ if empty(query.assignee) and empty(query.author) and empty(query.status) and empty(query.title) and empty(query.tags):
+ it = self.browser.iter_project_issues(query.project.id)
else:
it = self.browser.iter_issues(query)
- project = self.get_project(query.project)
for d in it:
- issue = self._make_issue(d, project)
+ issue = self._make_issue(d, query.project)
yield issue
def create_issue(self, project_id):
@@ -151,6 +169,8 @@ class GithubModule(Module, CapBugTracker):
issue.attachments = [self._make_attachment(dattach) for dattach in d['attachments']]
+ issue.tags = [t['name'] for t in d['labels']]
+
return issue
def _fetch_comments(self, issue):
@@ -159,6 +179,12 @@ class GithubModule(Module, CapBugTracker):
issue.history = []
issue.history += [self._make_comment(dcomment, issue.project) for dcomment in self.browser.iter_comments(project_id, issue_number)]
+ def _fetch_events(self, issue):
+ project_id, issue_number = self._extract_issue_id(issue.id)
+ if not issue.history:
+ issue.history = []
+ issue.history += [self._make_update(dcomment, issue.project) for dcomment in self.browser.iter_events(project_id, issue_number)]
+
def _make_attachment(self, d):
a = Attachment(d['url'])
a.url = d['url']
@@ -177,6 +203,22 @@ class GithubModule(Module, CapBugTracker):
u.attachments = [self._make_attachment(dattach) for dattach in d['attachments']]
return u
+ def _make_update(self, d, project):
+ u = Update(d['id'])
+ u.author = project.find_user(d['author'], None)
+ if not u.author:
+ # may duplicate users
+ u.author = User(d['author'], d['author'])
+ u.date = d['date']
+
+ c = Change()
+ c.field = d['field']
+ c.last = d['old']
+ c.new = d['new']
+
+ u.changes = [c]
+ return u
+
@staticmethod
def _extract_issue_id(_id):
return _id.rsplit('/', 1)
@@ -184,3 +226,15 @@ class GithubModule(Module, CapBugTracker):
@staticmethod
def _build_issue_id(project_id, issue_number):
return '%s/%s' % (project_id, issue_number)
+
+ def fill_issue(self, issue, fields):
+ if set(['history']) & set(fields):
+ new = self.get_issue(issue.id)
+ for f in fields:
+ if empty(getattr(issue, f)):
+ setattr(issue, f, getattr(new, f))
+
+ OBJECTS = {
+ Issue: fill_issue,
+ }
+
diff --git a/modules/jcvelaux/module.py b/modules/jcvelaux/module.py
index 34f72ce5af7c81fa78b476aa847920077aad5e5f..25cd80495580e2ff34befa2dec720c2c885447dc 100644
--- a/modules/jcvelaux/module.py
+++ b/modules/jcvelaux/module.py
@@ -22,7 +22,7 @@ from __future__ import unicode_literals
from collections import OrderedDict
from weboob.tools.backend import Module, BackendConfig
-from weboob.capabilities.base import StringField, UserError
+from weboob.capabilities.base import UserError
from weboob.capabilities.gauge import CapGauge, GaugeSensor, Gauge, GaugeMeasure, SensorNotFound
from weboob.tools.value import Value, ValueBackendPassword
@@ -36,7 +36,7 @@ SENSOR_TYPES = OrderedDict([('available_bikes', 'Available bikes'),
('available_bike_stands', 'Free stands'),
('bike_stands', 'Total stands')])
-CITIES = ("Paris", "Rouen", "Toulouse", "Luxembourg", "Valence", "Stockholm",
+CITIES = ("Rouen", "Toulouse", "Luxembourg", "Valence", "Stockholm",
"Goteborg", "Santander", "Amiens", "Lillestrom", "Mulhouse", "Lyon",
"Ljubljana", "Seville", "Namur", "Nancy", "Creteil", "Bruxelles-Capitale",
"Cergy-Pontoise", "Vilnius", "Toyama", "Kazan", "Marseille", "Nantes",
@@ -48,11 +48,6 @@ class BikeMeasure(GaugeMeasure):
return '' % self.level
-class BikeSensor(GaugeSensor):
- longitude = StringField('Longitude of the sensor')
- latitude = StringField('Latitude of the sensor')
-
-
class jcvelauxModule(Module, CapGauge):
NAME = 'jcvelaux'
DESCRIPTION = ('City bike renting availability information.\nCities: %s' %
@@ -64,7 +59,7 @@ class jcvelauxModule(Module, CapGauge):
BROWSER = VelibBrowser
- CONFIG = BackendConfig(Value('city', label='City', default='Paris',
+ CONFIG = BackendConfig(Value('city', label='City', default='Lyon',
choices=CITIES + ("ALL",)),
ValueBackendPassword('api_key', label='Optional API key',
default='', noprompt=True))
@@ -86,7 +81,7 @@ class jcvelauxModule(Module, CapGauge):
def _make_sensor(self, sensor_type, info, gauge):
id = '%s.%s' % (sensor_type, gauge.id)
- sensor = BikeSensor(id)
+ sensor = GaugeSensor(id)
sensor.gaugeid = gauge.id
sensor.name = SENSOR_TYPES[sensor_type]
sensor.address = '%s' % info['address']
diff --git a/modules/lameteoagricole/pages.py b/modules/lameteoagricole/pages.py
index 4ef6d8b6ac23f627f6661f2056d4545b86ebee27..2fa852f69b74deb04c732039ddfa1104a62d10ea 100644
--- a/modules/lameteoagricole/pages.py
+++ b/modules/lameteoagricole/pages.py
@@ -24,32 +24,8 @@ from datetime import date, time, datetime, timedelta
from weboob.browser.elements import method, ListElement, ItemElement
from weboob.browser.filters.standard import CleanText, Field
from weboob.browser.pages import HTMLPage
-from weboob.capabilities.base import FloatField, IntField, Field as BaseField
-from weboob.capabilities.weather import City, Forecast, Temperature, Current
-from weboob.tools.compat import quote, unicode
-
-
-class DIRECTION(object):
- S = 'South'
- N = 'North'
- E = 'East'
- W = 'West'
- SE = 'South-East'
- SW = 'South-West'
- NW = 'North-West'
- NE = 'North-East'
-
-
-class FullForecast(Forecast):
- wind_speed = IntField('Wind speed (in m/s)')
- wind_direction = BaseField('Wind direction', unicode)
- humidity = FloatField('Relative humidity ratio')
-
-
-class FullCurrent(Current):
- wind_speed = IntField('Wind speed (in m/s)')
- wind_direction = BaseField('Wind direction', unicode)
- humidity = FloatField('Relative humidity ratio')
+from weboob.capabilities.weather import City, Forecast, Temperature, Current, Direction
+from weboob.tools.compat import quote
class CitiesPage(HTMLPage):
@@ -65,7 +41,7 @@ class CitiesPage(HTMLPage):
obj_name = CleanText('.')
def obj_id(self):
- return quote(Field('name')(self).encode('utf-8'))
+ return quote(Field('name')(self))
def temp(v):
@@ -85,8 +61,8 @@ class WeatherPage(HTMLPage):
humidity = self.get_cell(self.titles['Humidité relative'], n)
obj.humidity = float(humidity.strip('%')) / 100
- direction = self.get_cell(self.titles['Direction du vent'], n)[-2:].replace('O', 'W')
- obj.wind_direction = getattr(DIRECTION, direction)
+ direction = self.get_cell(self.titles['Direction du vent'], n).replace('O', 'W')
+ obj.wind_direction = getattr(Direction, direction)
if 'Vitesse du vent' in self.titles:
speed_text = self.get_cell(self.titles['Vitesse du vent'], n)
@@ -94,7 +70,15 @@ class WeatherPage(HTMLPage):
speed_text = self.get_cell(self.titles['Vitesse Moy. du vent'], n)
else:
speed_text = self.get_cell(self.titles['Vitesse moyenne du vent'], n)
- obj.wind_speed = 1000 * int(speed_text.replace('km/h', '').strip())
+ obj.wind_speed = int(speed_text.replace('km/h', '').strip())
+
+ if 'Probabilité de précipitations' in self.titles:
+ txt = self.get_cell(self.titles['Probabilité de précipitations'], n)
+ obj.precipitation_probability = float(txt.strip('<%')) / 100
+
+ if 'Nébulosité' in self.titles:
+ txt = self.get_cell(self.titles['Nébulosité'], n).rstrip('%')
+ obj.cloud = int(round(float(txt) / 100 * 8))
class HourPage(WeatherPage):
@@ -106,7 +90,7 @@ class HourPage(WeatherPage):
def get_current(self):
fore = next(iter(self.iter_forecast()))
- ret = FullCurrent()
+ ret = Current()
for f in ('date', 'text', 'wind_direction', 'wind_speed', 'humidity'):
setattr(ret, f, getattr(fore, f))
ret.temp = fore.high
@@ -121,7 +105,7 @@ class HourPage(WeatherPage):
day_str = None
for n in range(len(self.doc.xpath('//table[@id="meteoHour"]/tr[1]/td'))):
- obj = FullForecast()
+ obj = Forecast()
t = time(int(self.get_cell(self.titles['Heure'], n).rstrip('h')), 0)
@@ -152,7 +136,7 @@ class Days5Page(WeatherPage):
self.titles[CleanText('.')(tr)] = n
for n in range(1, len(self.doc.xpath('//table[@id="meteo2"]/tr[1]/td'))):
- obj = FullForecast()
+ obj = Forecast()
obj.low = temp(int(self.get_cell(self.titles['Température Mini'], n).rstrip('°')))
obj.high = temp(int(self.get_cell(self.titles['Température Maxi'], n).rstrip('°')))
obj.date = d
@@ -178,7 +162,7 @@ class Days10Page(WeatherPage):
cols = len(self.doc.xpath('//table[@id="meteo2"]//td/table'))
for n in range(1, cols):
- obj = FullForecast()
+ obj = Forecast()
obj.low = temp(int(self.get_cell(self.titles['Température Mini'], n).rstrip('°C')))
obj.high = temp(int(self.get_cell(self.titles['Température Maxi'], n).rstrip('°C')))
obj.date = d