wetboobs: added module ilmatieteenlaitos

Signed-off-by: Matthieu Weber <mweber+weboob@free.fr>
Signed-off-by: Romain Bignon <romain@symlink.me>
This commit is contained in:
Matthieu Weber 2015-02-06 21:16:46 +02:00 committed by Romain Bignon
commit 72d10e4695
6 changed files with 281 additions and 0 deletions

View file

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2015 Matthieu Weber
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from .module import IlmatieteenlaitosModule
__all__ = ['IlmatieteenlaitosModule']

View file

@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2015 Matthieu Weber
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from weboob.browser.browsers import PagesBrowser
from weboob.browser.url import URL
from .pages import WeatherPage, SearchCitiesPage
__all__ = ['IlmatieteenlaitosBrowser']
class IlmatieteenlaitosBrowser(PagesBrowser):
BASEURL = 'http://ilmatieteenlaitos.fi'
cities = URL('/etusivu\?p_p_id=locationmenuportlet_WAR_fmiwwwweatherportlets&p_p_lifecycle=2&p_p_state=normal&'
'p_p_mode=view&p_p_cacheability=cacheLevelFull&term=(?P<pattern>.*)', SearchCitiesPage)
weather_query = URL('/paikallissaa\?p_p_id=locationmenuportlet_WAR_fmiwwwweatherportlets&p_p_lifecycle=1&'
'p_p_state=normal&p_p_mode=view&_locationmenuportlet_WAR_fmiwwwweatherportlets_action='
'changelocation')
weather = URL('/saa/(?P<city_url>.*)', WeatherPage)
def iter_city_search(self, pattern):
return self.cities.go(pattern=pattern).iter_cities()
def iter_forecast(self, city):
return self.weather_query.go(data={"place": city.name, "forecast": "short"}).iter_forecast()
def get_current(self, city):
return self.weather_query.go(data={"place": city.name, "forecast": "short"}).get_current()

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2015 Matthieu Weber
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from weboob.capabilities.weather import CapWeather, CityNotFound
from weboob.tools.backend import Module
from weboob.capabilities.base import find_object
from .browser import IlmatieteenlaitosBrowser
__all__ = ['IlmatieteenlaitosModule']
class IlmatieteenlaitosModule(Module, CapWeather):
NAME = 'ilmatieteenlaitos'
MAINTAINER = u'Matthieu Weber'
EMAIL = 'mweber+weboob@free.fr'
VERSION = '1.1'
DESCRIPTION = 'Get forecasts from the Ilmatieteenlaitos.fi website'
LICENSE = 'AGPLv3+'
BROWSER = IlmatieteenlaitosBrowser
def get_current(self, city_id):
return self.browser.get_current(self.get_city(city_id))
def iter_forecast(self, city_id):
return self.browser.iter_forecast(self.get_city(city_id))
def iter_city_search(self, pattern):
return self.browser.iter_city_search(pattern)
def get_city(self, _id):
return find_object(self.iter_city_search(_id), id=_id, error=CityNotFound)

View file

@ -0,0 +1,130 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2015 Matthieu Weber
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from datetime import date
from itertools import imap, ifilter
from weboob.browser.pages import JsonPage, HTMLPage
from weboob.browser.elements import ItemElement, ListElement, method
from weboob.capabilities.weather import Forecast, Current, City, Temperature
from weboob.browser.filters.json import Dict
from weboob.browser.filters.standard import Filter, CleanText, CleanDecimal, Regexp, Format, Date
class DictElement(ListElement):
def find_elements(self):
if self.item_xpath is not None:
for el in self.el:
yield el
else:
yield self.el
class Id(Filter):
def filter(self, txt):
return txt.split(", ")[0]
class SearchCitiesPage(JsonPage):
@method
class iter_cities(DictElement):
item_xpath = '.'
ignore_duplicate = True
class item(ItemElement):
klass = City
obj_id = Id(Dict('id'))
obj_name = Dict('value')
class WeatherPage(HTMLPage):
@method
class iter_forecast(ListElement):
item_xpath = ('//div[contains(@class, "mid") and contains(@class, "local-weather-forecast")]//'
'tr[@class="meteogram-dates"]/td')
class item(ItemElement):
klass = Forecast
obj_id = CleanText('.//span/@title')
def obj_date(self):
months = [u'tammikuuta', u'helmikuuta', u'maaliskuuta', u'huhtikuuta', u'toukokuuta', u'kesäkuuta',
u'heinäkuuta', u'elokuuta', u'syyskuuta', u'lokakuuta', u'marraskuuta', u'joulukuuta']
d = CleanText('.//span/@title')(self).split()
return date(int(d[2]), months.index(d[1])+1, int(d[0].strip(".")))
def temperatures(self):
offset = int(CleanText('string(sum(./preceding-sibling::td/@colspan))')(self))
length = int(CleanText('@colspan')(self))
temps = CleanText('../../../tbody/tr[@class="meteogram-temperatures"]/td[position() > %d '
'and position() <= %d]/span' % (offset, offset+length))(self)
return [float(_.strip(u'\xb0')) for _ in temps.split()]
def obj_low(self):
return Temperature(min(self.temperatures()), u'C')
def obj_high(self):
return Temperature(max(self.temperatures()), u'C')
def obj_text(self):
offset = int(CleanText('string(sum(./preceding-sibling::td/@colspan))')(self))
length = int(CleanText('@colspan')(self))
hour_test = ('../../tr[@class="meteogram-times"]/td[position() > %d and position() <= %d '
'and .//text() = "%%s"]' % (offset, offset+length))
hour_offset = 'string(count(%s/preceding-sibling::td)+1)' % (hour_test)
values = [
'../../../tbody/tr[@class="meteogram-weather-symbols"]/td[position() = %d]/div/@title',
'../../../tbody/tr[@class="meteogram-apparent-temperatures"]/td[position() = %d]/div/@title',
'../../../tbody/tr[@class="meteogram-wind-symbols"]/td[position() = %d]/div/@title',
'../../../tbody/tr[@class="meteogram-probabilities-of-precipitation"]/td[position() = %d]' +
'/div/@title',
'../../../tbody/tr[@class="meteogram-hourly-precipitation-values"]/td[position() = %d]/span/@title',
]
def descriptive_text_for_hour(hour):
hour_exists = CleanText(hour_test % hour)(self) == hour
if hour_exists:
offset = int(CleanText(hour_offset % hour)(self))
def info_for_value(value):
return CleanText(value % offset)(self).replace(u'edeltävän tunnin ', u'')
return ("klo %s: " % hour) + ", ".join(ifilter(bool, imap(info_for_value, values)))
return u'\n' + u'\n'.join(ifilter(bool, imap(descriptive_text_for_hour, ["02", "14"])))
@method
class get_current(ItemElement):
klass = Current
obj_id = date.today()
obj_date = Date(Regexp(CleanText('//table[@class="observation-text"]//span[@class="time-stamp"]'),
r'^(\d+\.\d+.\d+)'))
obj_text = Format(u'%s, %s, %s',
CleanText(u'(//table[@class="observation-text"])//tr[2]/td[2]'),
CleanText(u'(//table[@class="observation-text"])//tr[5]/td[1]'),
CleanText(u'(//table[@class="observation-text"])//tr[4]/td[2]'))
def obj_temp(self):
path = u'//table[@class="observation-text"]//span[@class="parameter-name" and text() = "Lämpötila"]' + \
u'/../span[@class="parameter-value"]'
temp = CleanDecimal(Regexp(CleanText(path), r'^([^ \xa0]+)'), replace_dots=True)(self)
unit = Regexp(CleanText(path), r'\xb0(\w)')(self)
return Temperature(float(temp), unit)

View file

@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2015 Matthieu Weber
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from weboob.tools.test import BackendTest
class IlmatieteenlaitosTest(BackendTest):
MODULE = 'ilmatieteenlaitos'
def test_ilmatieteenlaitos(self):
l = list(self.backend.iter_city_search('helsinki'))
self.assertTrue(len(l) > 0)
city = l[0]
current = self.backend.get_current(city.id)
self.assertTrue(current.temp.value > -50 and current.temp.value < 50)
forecasts = list(self.backend.iter_forecast(city.id))
self.assertTrue(len(forecasts) > 0)