Commit ffe0cb16 authored by Romain Bignon's avatar Romain Bignon

Update of modules

parent 0934cc7c
This diff is collapsed.
......@@ -25,10 +25,9 @@ from weboob.capabilities.video import CapVideo, BaseVideo
from weboob.capabilities.collection import CapCollection, CollectionNotFound, Collection
from weboob.tools.backend import Module, BackendConfig
from weboob.tools.value import Value
from weboob.exceptions import BrowserHTTPSDowngrade
from .browser import ArteBrowser
from .video import ArteVideo, ArteSiteVideo, VERSION_VIDEO, FORMATS, LANG, QUALITY, SITE
from .video import VERSION_VIDEO, FORMATS, LANG, QUALITY, SITE, get_site_enum_by_id
__all__ = ['ArteModule']
......@@ -69,36 +68,22 @@ class ArteModule(Module, CapVideo, CapCollection):
version=self.config['version'].get())
def parse_id(self, _id):
sites = '|'.join(k.get('id') for k in SITE.values)
m = re.match('^(%s)\.(.*)' % sites, _id)
m = re.match('https?://www.arte.tv/\w+/videos/(?P<id>.+)/(.*)', _id)
if m:
return m.groups()
m = re.match('https?://www.arte.tv/guide/\w+/(?P<id>.+)/(.*)', _id)
if m:
return SITE.PROGRAM.get('id'), m.group(1)
m = re.match('https?://(%s).arte.tv/(\w+)/(.*)' % (sites), _id)
if m:
return m.group(1), '/%s/%s' % (m.group(2), m.group(3))
return m.group(1)
if not _id.startswith('http'):
return 'videos', _id
return _id
return None, None
return None
def get_video(self, _id):
site, _id = self.parse_id(_id)
def get_video(self, _id, video=None):
_id = self.parse_id(_id)
if not (site and _id):
if _id is None:
return None
if site in [value.get('id') for value in SITE.values]:
_site = (value for value in SITE.values if value.get('id') == site).next()
return getattr(self.browser, _site.get('video'))(_id)
else:
return self.browser.get_video(_id)
return self.browser.get_video(_id, video)
def search_videos(self, pattern, sortby=CapVideo.SEARCH_RELEVANCE, nsfw=False):
return self.browser.search_videos(pattern)
......@@ -109,60 +94,48 @@ class ArteModule(Module, CapVideo, CapCollection):
if 'thumbnail' in fields and video and video.thumbnail:
video.thumbnail.data = self.browser.open(video.thumbnail.url).content
if 'url' in fields and not video.url:
video.ext, video.url = self.browser.fetch_url(video.id)
return video
def fill_site_video(self, video, fields):
if fields != ['thumbnail']:
for site in SITE.values:
m = re.match('%s\.(.*)' % site.get('id'), video.id)
if m:
video = getattr(self.browser, site.get('video'))(m.group(1), video)
break
if 'thumbnail' in fields and video and video.thumbnail:
try:
video.thumbnail.data = self.browser.open(video.thumbnail.url).content
except BrowserHTTPSDowngrade:
pass
return video
def iter_resources(self, objs, split_path):
collection = self.get_collection(objs, split_path)
if BaseVideo in objs:
collection = self.get_collection(objs, split_path)
if collection.path_level == 0:
yield Collection([u'arte-latest'], u'Latest Arte videos')
for site in SITE.values:
yield Collection([site.get('id')], site.get('label'))
if collection.path_level == 1:
if collection.split_path == [u'arte-latest']:
for video in self.browser.latest_videos():
yield video
return [Collection([site.get('id')], site.get('label')) for site in SITE.values]
elif len(split_path) == 1:
site = get_site_enum_by_id(split_path[0])
subsite = site.get('enum', None)
if site == SITE.PROGRAM:
return self.browser.get_arte_programs(split_path)
elif site == SITE.GUIDE:
return self.browser.get_arte_guide_days(split_path)
elif site == SITE.CREATIVE:
return self.browser.get_arte_creative_videos()
elif subsite:
return self.browser.get_arte_generic_subsites(split_path, subsite)
elif collection.path_level > 1:
site = get_site_enum_by_id(split_path[0])
if site == SITE.GUIDE:
return self.browser.get_arte_guide_videos(split_path)
else:
for site in SITE.values:
if collection.split_path[0] == site.get('id') and collection.path_level in site.keys():
for item in getattr(self.browser, site.get(collection.path_level))():
yield item
subsite = site.get('enum', {})
if subsite:
subsite = dict(subsite.items)
if collection.path_level >= 2:
for site in SITE.values:
if collection.split_path[0] == site.get('id') and collection.path_level in site.keys():
for item in getattr(self.browser, site.get(collection.path_level))(collection.split_path):
yield item
return self.browser.get_arte_navigation(split_path,
subsite.get('COLLECTION', {}).get('id', r''),
subsite.get('PLAYLIST', {}).get('id', r''),
subsite.get('MAGAZINE', {}).get('id', r''))
def validate_collection(self, objs, collection):
if collection.path_level == 0:
return
if BaseVideo in objs and (collection.split_path == [u'arte-latest'] or
collection.split_path[0] in [value.get('id') for value in SITE.values]):
return
if BaseVideo in objs and collection.path_level >= 2 and\
collection.split_path[0] in [value.get('id') for value in SITE.values]:
if BaseVideo in objs and (collection.split_path[0] in [value.get('id') for value in SITE.values]):
return
raise CollectionNotFound(collection.split_path)
OBJECTS = {ArteVideo: fill_arte_video, ArteSiteVideo: fill_site_video}
OBJECTS = {BaseVideo: fill_arte_video}
This diff is collapsed.
......@@ -21,7 +21,7 @@
from weboob.tools.test import BackendTest
from weboob.tools.value import Value
from weboob.capabilities.video import BaseVideo
from .video import SITE, ArteSiteVideo
from .video import SITE
class ArteTest(BackendTest):
......@@ -45,42 +45,16 @@ class ArteTest(BackendTest):
def test_sites(self):
for site in SITE.values:
if site.get('id') == SITE.PROGRAM.get('id'):
continue
l1 = list(self.backend.iter_resources([BaseVideo], [site.get('id')]))
assert len(l1)
while not isinstance(l1[0], BaseVideo):
l1 = list(self.backend.iter_resources([BaseVideo], l1[0].split_path))
l1 = list(self.backend.iter_resources([BaseVideo], l1[-1].split_path))
assert len(l1)
for v in l1:
v = self.backend.fillobj(v, ('url',))
if type(v) == ArteSiteVideo:
if type(v) == BaseVideo:
exit
self.assertTrue(v.url, 'URL for video "%s" not found' % (v.id))
def test_latest(self):
l = list(self.backend.iter_resources([BaseVideo], [u'arte-latest']))
assert len(l)
v = l[0]
self.backend.fillobj(v, ('url',))
self.assertTrue(v.url, 'URL for video "%s" not found' % (v.id))
def test_program(self):
l1 = list(self.backend.iter_resources([BaseVideo], [u'program']))
assert len(l1)
# some categories may contain no available videos (during summer period for example)
for l in l1:
l2 = list(self.backend.iter_resources([BaseVideo], l.split_path))
if len(l2) == 0:
continue
break
assert len(l2)
v = l2[0]
self.backend.fillobj(v, ('url',))
self.assertTrue(v.url, 'URL for video "%s" not found' % (v.id))
......@@ -17,8 +17,6 @@
# 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.video import BaseVideo
def enum(**enums):
_values = list(enums.values())
......@@ -41,14 +39,63 @@ FORMATS = enum(HTTP_MP4=u'HBBTV', HLS=u'M3U8', RTMP=u'RTMP', HLS_MOBILE=u'MOBILE
LANG = enum(FRENCH={u'label': u'French', u'webservice': u'F', u'site': u'fr', u'version': u'1', u'title': u'titleFR'},
GERMAN={u'label': u'German', u'webservice': u'D', u'site': u'de', u'version': u'1', u'title': u'titleDE'})
SITE = enum(PROGRAM={u'id': u'program', u'label': u'Arte Programs', 1: 'get_arte_programs',
2: 'get_arte_program_videos', u'video': 'get_video_from_program_id'},
CONCERT={u'id': u'concert', u'label': u'Arte Concert videos', 1: 'get_arte_concert_categories',
2: 'get_arte_concert_videos', 'video': 'get_arte_concert_video'},
CINEMA={u'id': u'cinema', u'label': u'Arte Cinema', 1: 'get_arte_cinema_categories',
2: 'get_arte_cinema_categories', 3: 'get_arte_cinema_videos', 'video': 'get_arte_cinema_video'},
CREATIVE={u'id': u'creative', u'label': u'Arte Creative', 1: 'get_arte_creative_categories',
2: 'get_arte_creative_videos', 'video': 'get_arte_creative_video'})
CONCERT = enum(CLASSIQUE={u'id': u'CLA', u'label': u'Classique'},
ACTUELLE={u'id': u'MUA', u'label': u'Musiques actuelles'},
OPERA={u'id': u'OPE', u'label': u'Opera'},
JAZZ={u'id': u'JAZ', u'label': u'Jazz'},
MONDE={u'id': u'MUD', u'label': u'Musiques du monde'},
SCENE={u'id': u'ADS', u'label': u'Arts de la scène'},
COLLECTION={u'id': u'collections_ARS', u'label': u'Collections'},
PLAYLIST={u'id': u'playlists_ARS', u'label': u'Playlists'})
CINEMA = enum(FILM={u'id': u'FLM', u'label': u'Films'},
CLASSIQUES={u'id': u'MCL', u'label': u'Les grands du 7e art'},
COURT_METRAGES={u'id': u'CMG', u'label': u'Courts métrages'},
FILM_MUETS={u'id': u'CMU', u'label': u'Films muets'},
ACTU={u'id': u'ACC', u'label': u'Actualité du cinéma'},
COLLECTION={u'id': u'collections_CIN', u'label': u'Collections'},
MAGAZINE={u'id': u'magazines_CIN', u'label': u'Émissions'})
SERIE = enum(SERIES={u'id': u'SES', u'label': u'Séries'},
FICTIONS={u'id': u'FIC', u'label': u'Fictions'},
HUMOUR={u'id': u'CHU', u'label': u'Courts humoristiques'},
COLLECTION={u'id': u'collections_SER', u'label': u'Collections'})
POP = enum(POP={u'id': u'POP', u'label': u'Culture pop'},
ART={u'id': u'ART', u'label': u'Arts'},
IDE={u'id': u'IDE', u'label': u'Idées'},
COLLECTION={u'id': u'collections_CPO', u'label': u'Collections'},
MAGAZINE={u'id': u'magazines_CPO', u'label': u'Émissions'})
SCIENCE = enum(POP={u'id': u'SAN', u'label': u'Médecine et santé'},
EEN={u'id': u'ENN', u'label': u'Environnement et nature'},
TEC={u'id': u'TEC', u'label': u'Technologies et innovations'},
ENB={u'id': u'ENB', u'label': u'En bref'},
COLLECTION={u'id': u'collections_SCI', u'label': u'Collections'},
MAGAZINE={u'id': u'magazines_SCI', u'label': u'Émissions'})
VOYAGE = enum(NEA={u'id': u'NEA', u'label': u'Nature et animaux'},
EVA={u'id': u'EVA', u'label': u'Evasion'},
ATA={u'id': u'ATA', u'label': u'A table !'},
VIA={u'id': u'VIA', u'label': u'Vies d\'ailleurs'},
COLLECTION={u'id': u'collections_DEC', u'label': u'Collections'},
MAGAZINE={u'id': u'magazines_DEC', u'label': u'Émissions'})
HISTOIRE = enum(XXE={u'id': u'XXE', u'label': u'XXe siècle'},
CIV={u'id': u'CIV', u'label': u'Civilisations'},
LGP={u'id': u'LGP', u'label': u'Les grands personnages'},
COLLECTION={u'id': u'collections_DEC', u'label': u'Collections'})
SITE = enum(PROGRAM={u'id': u'program', u'label': u'Arte Programs'},
CREATIVE={u'id': u'creative', u'label': u'Arte Creative'},
GUIDE={u'id': u'guide', u'label': u'Arte Guide TV'},
CONCERT={u'id': u'concert', u'label': u'Arte Concert videos', u'enum': CONCERT},
CINEMA={u'id': u'cinema', u'label': u'Arte Cinema', u'enum': CINEMA},
SERIE={u'id': u'series-et-fictions', u'label': u'Arte CreativeSéries et fictions', u'enum': SERIE},
POP={u'id': u'culture-et-pop', u'label': u'Culture et pop', u'enum': POP},
SCIENCE={u'id': u'sciences', u'label': u'Sciences', u'enum': SCIENCE},
VOYAGE={u'id': u'voyages-et-decouvertes', u'label': u'Voyages et découvertes', u'enum': VOYAGE},
HISTOIRE={u'id': u'histoire', u'label': u'Histoire', u'enum': HISTOIRE})
QUALITY = enum(HD={'label': u'SQ', 'order': 3},
MD={'label': u'EQ', 'order': 2},
......@@ -66,14 +113,8 @@ VERSION_VIDEO = enum(VOSTA={u'label': u'Original version subtitled (German)', LA
VFSTMF={u'label': u'Deaf version (French)', LANG.FRENCH.get('label'): u'8'})
class ArteVideo(BaseVideo):
pass
class ArteSiteVideo(BaseVideo):
pass
class ArteEmptyVideo(BaseVideo):
def __init__(self):
self.description = u'There is no video on this page'
def get_site_enum_by_id(id):
for s in SITE.values:
if s.get('id') == id:
return s
return
This diff is collapsed.
This diff is collapsed.
......@@ -19,7 +19,7 @@
from random import randint
from weboob.deprecated.browser import BrowserUnavailable
from weboob.exceptions import BrowserUnavailable
from weboob.capabilities.dating import Optimization
from weboob.tools.log import getLogger
......@@ -59,10 +59,9 @@ class ProfilesWalker(Optimization):
def enqueue_profiles(self):
try:
with self._browser:
profiles_to_visit = self._browser.search_profiles().difference(self._visited_profiles)
self._logger.info(u'Enqueuing profiles to visit: %s' % profiles_to_visit)
self._profiles_queue = set(profiles_to_visit)
profiles_to_visit = self._browser.search_profiles().difference(self._visited_profiles)
self._logger.info(u'Enqueuing profiles to visit: %s' % profiles_to_visit)
self._profiles_queue = set(profiles_to_visit)
self.save()
except BrowserUnavailable:
return
......@@ -75,8 +74,7 @@ class ProfilesWalker(Optimization):
return # empty queue
try:
with self._browser:
profile = self._browser.get_profile(id)
profile = self._browser.get_profile(id)
self._logger.info(u'Visited profile %s (%s)' % (profile['pseudo'], id))
# Get score from the aum_score module
......
......@@ -18,7 +18,7 @@
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from weboob.deprecated.browser import BrowserUnavailable
from weboob.exceptions import BrowserUnavailable
from weboob.capabilities.dating import Optimization
from weboob.capabilities.contact import QueryError
from weboob.tools.log import getLogger
......@@ -81,13 +81,12 @@ class QueriesQueue(Optimization):
if not id:
continue
with self._browser:
if self._browser.send_charm(id):
self._logger.info('Charm sent to %s', id)
else:
self._queue.append((priority, id))
self._logger.info("Charm can't be send to %s", id)
break
if self._browser.send_charm(id):
self._logger.info('Charm sent to %s', id)
else:
self._queue.append((priority, id))
self._logger.info("Charm can't be send to %s", id)
break
# As the charm has been correctly sent (no exception raised),
# we don't store anymore ID, because if nbAvailableCharms()
......
......@@ -18,7 +18,7 @@
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from weboob.deprecated.browser import BrowserUnavailable
from weboob.exceptions import BrowserUnavailable
from weboob.capabilities.dating import Optimization
......@@ -43,7 +43,6 @@ class Visibility(Optimization):
def reconnect(self):
try:
with self._browser:
self._browser.login()
self._browser.login()
except BrowserUnavailable:
pass
......@@ -19,7 +19,7 @@
from weboob.tools.test import BackendTest
from weboob.deprecated.browser import BrowserUnavailable
from weboob.exceptions import BrowserUnavailable
class AuMTest(BackendTest):
......
# -*- coding: utf-8 -*-
# Copyright(C) 2012 Noé Rubinstein
# Copyright(C) 2017 ZeHiro
#
# This file is part of weboob.
#
......@@ -17,7 +17,10 @@
# 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 __future__ import unicode_literals
from .module import MangagoModule
__all__ = ['MangagoModule']
from .module import AvendrealouerModule
__all__ = ['AvendrealouerModule']
# -*- coding: utf-8 -*-
# Copyright(C) 2017 ZeHiro
#
# 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 __future__ import unicode_literals
from weboob.browser import PagesBrowser, URL
from weboob.capabilities.housing import HOUSE_TYPES
from .pages import CitiesPage, SearchPage, HousingPage
from .constants import QUERY_TYPES, QUERY_HOUSE_TYPES
class AvendrealouerBrowser(PagesBrowser):
BASEURL = 'https://www.avendrealouer.fr'
cities = URL(r'/common/api/localities\?term=(?P<term>)', CitiesPage)
search = URL(r'/recherche.html\?pageIndex=1&sortPropertyName=Price&sortDirection=Ascending&searchTypeID=(?P<type_id>.*)&typeGroupCategoryID=1&transactionId=1&localityIds=(?P<location_ids>.*)&typeGroupIds=(?P<type_group_ids>.*)(?P<rooms>.*)(?P<min_price>.*)(?P<max_price>.*)(?P<min_surface>.*)(?P<max_surface>.*)', SearchPage)
search_one = URL(r'/recherche.html\?localityIds=4-36388&reference=(?P<reference>.*)&hasMoreCriterias=true&searchTypeID=1', SearchPage)
housing = URL(r'/[vente|location].*', HousingPage)
def get_cities(self, pattern):
return self.cities.open(term=pattern).iter_cities()
def search_housings(self, query):
type_id = QUERY_TYPES[query.type]
house_types = []
for house_type in query.house_types:
if house_type == HOUSE_TYPES.UNKNOWN:
house_types = QUERY_HOUSE_TYPES[house_type]
break
house_types.append(QUERY_HOUSE_TYPES[house_type])
type_group_ids = ','.join(house_types)
location_ids = ','.join([city.id for city in query.cities])
def build_optional_param(query_field, query_string):
replace = ''
if getattr(query, query_field):
replace = '&%s=%s' % (query_string, getattr(query, query_field))
return replace
rooms = ''
if query.nb_rooms:
rooms = str(query.nb_rooms)
for i in range(query.nb_rooms + 1, 6):
rooms += ',%s' % str(i)
rooms = '&roomComfortIds=%s' % rooms
reg_exp = {
'type_id': type_id,
'type_group_ids': type_group_ids,
'location_ids': location_ids,
'rooms': rooms,
'min_price': build_optional_param('cost_min', 'minimumPrice'),
'max_price': build_optional_param('cost_max', 'maximumPrice'),
'min_surface': build_optional_param('area_min', 'minimumSurface'),
'max_surface': build_optional_param('area_max', 'maximumSurface')
}
return self.search.open(**reg_exp).iter_housings()
def get_housing(self, housing_id, obj=None):
url = self.search_one.open(reference=housing_id).get_housing_url()
return self.open(url).page.get_housing(obj=obj)
from weboob.capabilities.housing import HOUSE_TYPES, POSTS_TYPES
QUERY_TYPES = {
POSTS_TYPES.RENT: 2,
POSTS_TYPES.SALE: 1,
POSTS_TYPES.SHARING: 2, # There is no special search for shared appartments.
POSTS_TYPES.FURNISHED_RENT: 2,
POSTS_TYPES.VIAGER: 1
}
QUERY_HOUSE_TYPES = {
HOUSE_TYPES.APART: ['1'],
HOUSE_TYPES.HOUSE: ['2'],
HOUSE_TYPES.PARKING: ['7'],
HOUSE_TYPES.LAND: ['3'],
HOUSE_TYPES.OTHER: ['4', '5', '6', '8', '9', '10', '11', '12', '13', '14'],
HOUSE_TYPES.UNKNOWN: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14']
}
# -*- coding: utf-8 -*-
# Copyright(C) 2012 Noé Rubinstein
# Copyright(C) 2017 ZeHiro
#
# This file is part of weboob.
#
......@@ -17,20 +17,62 @@
# 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 __future__ import unicode_literals
from weboob.tools.capabilities.gallery.genericcomicreader import GenericComicReaderModule, DisplayPage
__all__ = ['MangagoModule']
from weboob.tools.backend import Module
from weboob.capabilities.housing import CapHousing, Housing
from .browser import AvendrealouerBrowser
class MangagoModule(GenericComicReaderModule):
NAME = 'mangago'
DESCRIPTION = 'Mangago manga reading site'
DOMAIN = 'www.mangago.com'
BROWSER_PARAMS = dict(
img_src_xpath="//div[@id='pic_container']//img/@src",
page_list_xpath="(//div[contains(@class,'page_select')]//select)[1]//@value")
ID_REGEXP = r'(?:[^/]+/)+.+'
URL_REGEXP = r'.+mangago.com/r/l_manga/(%s).+' % ID_REGEXP
ID_TO_URL = 'http://www.mangago.com/r/l_manga/%s'
PAGES = { URL_REGEXP: DisplayPage }
__all__ = ['AvendrealouerModule']
class AvendrealouerModule(Module, CapHousing):
NAME = u'avendrealouer'
DESCRIPTION = 'avendrealouer website'
MAINTAINER = 'ZeHiro'
EMAIL = 'public@abossy.fr'
LICENSE = 'AGPLv3+'
VERSION = '1.4'
BROWSER = AvendrealouerBrowser
def get_housing(self, housing):
"""
Get an housing from an ID.
:param housing: ID of the housing
:type housing: str
:rtype: :class:`Housing` or None if not found.
"""
return self.browser.get_housing(housing)
def search_city(self, pattern):
"""
Search a city from a pattern.
:param pattern: pattern to search
:type pattern: str
:rtype: iter[:class:`City`]
"""
return self.browser.get_cities(pattern)
def search_housings(self, query):
"""
Search housings.
:param query: search query
:type query: :class:`Query`
:rtype: iter[:class:`Housing`]
"""
return self.browser.search_housings(query)
def fill_housing(self, housing, fields):
if 'photos' in fields and housing.photos:
for photo in housing.photos:
photo.data = self.browser.open(photo.url)
return housing
OBJECTS = {Housing: fill_housing}
# -*- coding: utf-8 -*-
# Copyright(C) 2017 ZeHiro
#
# 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 __future__ import unicode_literals
from datetime import datetime
from weboob.browser.pages import HTMLPage, JsonPage, pagination
from weboob.browser.elements import ItemElement, ListElement, method, DictElement
from weboob.browser.filters.html import Attr, AbsoluteLink, Link
from weboob.browser.filters.json import Dict
from weboob.browser.filters.standard import CleanDecimal, CleanText, Date, Regexp, Async, AsyncLoad
from weboob.capabilities.housing import City, Housing, UTILITIES, HousingPhoto
from weboob.capabilities.base import NotAvailable, Currency
from weboob.tools.capabilities.housing.housing import PricePerMeterFilter
class CitiesPage(JsonPage):
@method
class iter_cities(DictElement):
class item(ItemElement):
klass = City
obj_id = Dict('Value')
obj_name = Dict('Name')
class AvendreAlouerItem(ItemElement):
klass = Housing
_url = AbsoluteLink('.//a[has-class("linkCtnr")]')
load_details = _url & AsyncLoad
obj_url = _url
obj_id = Async('details') & CleanText(Regexp(CleanText('//p[has-class("property-reference")]'), r'\:(.*)$', default=''))
obj_title = CleanText('.//a//ul')
obj_area = CleanDecimal(
CleanText('.//a//ul//li[has-class("first")]//following-sibling::li[2]'),
default=NotAvailable
)
obj_cost = CleanDecimal(
CleanText('.//span[has-class("price")]')
)
obj_price_per_meter = PricePerMeterFilter()
obj_currency = CleanText(
Regexp(
CleanText('.//span[has-class("price")]'),
r'[\d\ ]+(.*)'
)
)
obj_location = CleanText('.//span[has-class("loca")]')
obj_text = CleanText('.//p[has-class("propShortDesc")]')
obj_date = Async('details') & Date(
Regexp(
CleanText('//div[has-class("property-description-main")]'),
r'Mise à jour le ([\d\\]+)', default=datetime.today()
)
)
def obj_details(self):
page_doc = Async('details').loaded_page(self).doc
return {
'GES': CleanText('//span[@id="gassymbol"]', '')(page_doc),