The new woob repository is here: https://gitlab.com/woob/woob. This gitlab will be removed soon.

Commit 8b2d0afd authored by Vincent A's avatar Vincent A

backport devel modules fixes

parent 51af15e3
Pipeline #3720 passed with stages
in 65 minutes and 10 seconds
# -*- coding: utf-8 -*-
# Copyright(C) 2018 Antoine BOSSY
#
# This file is part of woob.
#
# woob 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.
#
# woob 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 woob. If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from .module import BieniciModule
__all__ = ['BieniciModule']
# -*- coding: utf-8 -*-
# Copyright(C) 2018 Antoine BOSSY
#
# This file is part of woob.
#
# woob 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.
#
# woob 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 woob. If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from woob.browser import PagesBrowser, URL
from woob.tools.json import json
from woob.capabilities.housing import POSTS_TYPES, HOUSE_TYPES
from .pages import Cities, ResultsPage, HousingPage
TRANSACTION_TYPE = {
POSTS_TYPES.SALE: 'buy',
POSTS_TYPES.RENT: 'rent',
POSTS_TYPES.SALE: 'buy',
POSTS_TYPES.FURNISHED_RENT: 'rent',
POSTS_TYPES.SHARING: 'rent'
}
HOUSE_TYPES = {
HOUSE_TYPES.APART: ['flat'],
HOUSE_TYPES.HOUSE: ['house'],
HOUSE_TYPES.PARKING: ['parking'],
HOUSE_TYPES.LAND: ['terrain'],
HOUSE_TYPES.OTHER: ['others', 'loft', 'shop', 'building', 'castle', 'premises', 'office', 'townhouse'],
HOUSE_TYPES.UNKNOWN: []
}
class BieniciBrowser(PagesBrowser):
BASEURL = 'https://www.bienici.com'
cities = URL(r'https://res.bienici.com/suggest.json\?q=(?P<zipcode>.+)', Cities)
results = URL(r'/realEstateAds.json\?filters=(?P<filters>.+)', ResultsPage)
housing = URL(r'/realEstateAds-one.json\?filters=(?P<filters>.*)&onlyRealEstateAd=(?P<housing_id>.*)', HousingPage)
def get_cities(self, pattern):
return self.cities.go(zipcode=pattern).get_city()
def search_housing(self, query):
filters = {
'size': 100,
'page': 1,
'resultsPerPage': 24,
'maxAuthorizedResults': 2400,
'sortBy': "relevance",
'sortOrder': "desc",
'onTheMarket': [True],
'showAllModels': False,
"zoneIdsByTypes": {
'zoneIds': []
},
'propertyType': []
}
dict_query = query.to_dict()
if dict_query['area_min']:
filters['minArea'] = dict_query['area_min']
if dict_query['area_max']:
filters['maxArea'] = dict_query['area_max']
if dict_query['cost_min']:
filters['minPrice'] = dict_query['cost_min']
if dict_query['cost_max']:
filters['maxPrice'] = dict_query['cost_max']
filters['filterType'] = TRANSACTION_TYPE[dict_query['type']]
for housing_type in dict_query['house_types']:
filters['propertyType'] += HOUSE_TYPES[housing_type]
for city in dict_query['cities']:
filters['zoneIdsByTypes']['zoneIds'].append(city.id)
return self.results.go(filters=json.dumps(filters)).get_housings()
def get_housing(self, housing_id):
# This is to serialize correctly the JSON, and match the URL easier.
filters = {
'onTheMarket': [True]
}
return self.housing.go(housing_id=housing_id, filters=json.dumps(filters)).get_housing()
# -*- coding: utf-8 -*-
# Copyright(C) 2018 Antoine BOSSY
#
# This file is part of woob.
#
# woob 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.
#
# woob 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 woob. If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from woob.tools.backend import Module
from woob.capabilities.housing import CapHousing, Housing, HousingPhoto
from .browser import BieniciBrowser
__all__ = ['BieniciModule']
class BieniciModule(Module, CapHousing):
NAME = 'bienici'
DESCRIPTION = 'bienici website'
MAINTAINER = 'Antoine BOSSY'
EMAIL = 'mail+github@abossy.fr'
LICENSE = 'AGPLv3+'
VERSION = '3.0'
BROWSER = BieniciBrowser
def get_housing(self, id):
"""
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(id)
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_housing(query)
def fill_photo(self, photo, fields):
"""
Fills the photo.
"""
if 'data' in fields and photo.url and not photo.data:
photo.data = self.browser.open(photo.url).content
return photo
def fill_housing(self, housing, fields):
"""
Fills the housing.
"""
return self.get_housing(housing.id)
OBJECTS = {HousingPhoto: fill_photo, Housing: fill_housing}
# -*- coding: utf-8 -*-
# Copyright(C) 2018 Antoine BOSSY
#
# This file is part of woob.
#
# woob 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.
#
# woob 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 woob. If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from woob.browser.pages import JsonPage
from woob.browser.elements import ItemElement, DictElement, method
from woob.browser.filters.json import Dict, ItemNotFound
from woob.capabilities.base import NotAvailable
from woob.browser.filters.standard import CleanText, Date, CleanDecimal
from woob.capabilities.housing import City, Housing, HousingPhoto, ENERGY_CLASS
class Cities(JsonPage):
@method
class get_city(DictElement):
class item(ItemElement):
klass = City
obj_id = Dict('zoneIds/0')
obj_name = CleanText(Dict('name'))
class MyItemElement(ItemElement):
klass = Housing
def condition(self):
return not Dict('userRelativeData/isAdModifier')(self)
obj_id = Dict('id')
obj_title = Dict('title')
obj_area = Dict('surfaceArea')
obj_cost = Dict('price')
def obj_price_per_meter(self):
try:
return Dict('pricePerSquareMeter')(self)
except ItemNotFound:
return NotAvailable
obj_currency = 'EUR'
obj_date = Date(Dict('publicationDate'))
obj_location = CleanDecimal(Dict('postalCode'))
obj_text = Dict('description', '')
def obj_photos(self):
return [HousingPhoto(photo['url']) for photo in Dict('photos')(self)]
obj_rooms = Dict('roomsQuantity', 0)
obj_bedrooms = Dict('bedroomsQuantity', 0)
def obj_DPE(self):
try:
return ENERGY_CLASS[Dict('energyClassification')(self)]
except (KeyError, ItemNotFound):
return NotAvailable
def obj_GES(self):
try:
return ENERGY_CLASS[Dict('greenhouseGazClassification')(self)]
except (KeyError, ItemNotFound):
return NotAvailable
class ResultsPage(JsonPage):
@method
class get_housings(DictElement):
item_xpath = 'realEstateAds'
class item(MyItemElement):
pass
class HousingPage(JsonPage):
@method
class get_housing(MyItemElement):
pass
This diff is collapsed.
......@@ -117,7 +117,7 @@ def new_recipient(self, recipient, **params):
return self.browser.new_recipient(recipient, **params)
def init_transfer(self, transfer, **params):
if {'Clé', 'resume'} & set(params.keys()):
if {'Clé', 'resume', 'code'} & set(params.keys()):
return self.browser.continue_transfer(transfer, **params)
# There is a check on the website, transfer can't be done with too long reason.
......
......@@ -859,8 +859,14 @@ class Transaction(FrenchTransaction):
(re.compile(r'^RETRAIT DAB (?P<dd>\d{2})(?P<mm>\d{2}) (?P<text>.*) CARTE [\*\d]+'), FrenchTransaction.TYPE_WITHDRAWAL),
(re.compile(r'^(?P<dd>\d{2})/(?P<mm>\d{2})/(?P<yy>\d{4}) RETRAIT DAB (?P<text>.*)'), FrenchTransaction.TYPE_WITHDRAWAL),
(re.compile(r'^CHEQUE( (?P<text>.*))?$'), FrenchTransaction.TYPE_CHECK),
(re.compile(r'^FACTURE SGT.*'), FrenchTransaction.TYPE_BANK),
(re.compile(r'^(F )?COTIS\.? (?P<text>.*)'), FrenchTransaction.TYPE_BANK),
(re.compile(r'^(F )?RETRO\.? (?P<text>.*)'), FrenchTransaction.TYPE_BANK),
(re.compile(r'^EXT.AGIOS'), FrenchTransaction.TYPE_BANK),
(re.compile(r'^(?P<text>(?P<category>INTERETS).*)'), FrenchTransaction.TYPE_BANK),
(re.compile(r'(?P<text>PREL\.(SOC|OBL).*)'), FrenchTransaction.TYPE_BANK),
(re.compile(r'^(REMISE|REM CHQ) (?P<text>.*)'), FrenchTransaction.TYPE_DEPOSIT),
(re.compile(r'^VERSEMT PERIOD'), FrenchTransaction.TYPE_DEPOSIT),
(re.compile(r'^(?P<text>(ÉCHÉANCE|Echéance)).*'), FrenchTransaction.TYPE_LOAN_PAYMENT),
]
......@@ -873,7 +879,7 @@ def go_on_history_tab(self):
# Maybe obsolete
form = self.get_form(id='I1:fm')
except FormNotFound:
form = self.get_form(id='I1:P:F')
form = self.get_form(id='I1:P1:F')
form['_FID_DoShowListView'] = ''
form.submit()
......@@ -1780,7 +1786,7 @@ def fill_iban(self, accounts):
def get_iban_document(self, subscription):
for raw in self.doc.xpath('//table[has-class("liste")]//tbody//tr[not(@class)]'):
if raw.xpath('.//td[1]')[0].text_content().startswith(subscription.label.upper()):
if raw.xpath('.//td[1]')[0].text_content().upper().startswith(subscription.label.upper()):
iban_document = Document()
iban_document.label = 'IBAN {}'.format(subscription.label)
iban_document.url = Link(raw.xpath('.//a'))(self.doc)
......@@ -2061,10 +2067,7 @@ def obj_id(self):
return '%s%s' % (bank_info, partial_number)
class InternalTransferPage(LoggedPage, HTMLPage, AppValidationPage):
RECIPIENT_STRING = 'data_input_indiceCompteACrediter'
READY_FOR_TRANSFER_MSG = 'Confirmer un virement entre vos comptes'
SUMMARY_RECIPIENT_TITLE = 'Compte à créditer'
class TransferPageCommon(LoggedPage, HTMLPage, AppValidationPage):
IS_PRO_PAGE = False
def needs_personal_key_card_validation(self):
......@@ -2073,6 +2076,12 @@ def needs_personal_key_card_validation(self):
def needs_otp_validation(self):
return bool(self.doc.xpath('//input[@name="otp_password"]'))
def get_transfer_code_form(self):
form = self.get_form(id='P:F')
code_form = dict(form.items())
code_form['url'] = form.url
return code_form
def can_transfer_pro(self, origin_account):
for li in self.doc.xpath('//ul[@id="idDetailsListCptDebiterVertical:ul"]//ul/li'):
if CleanText(li.xpath('.//span[@class="_c1 doux _c1"]'), replace=[(' ', '')])(self) in origin_account:
......@@ -2087,27 +2096,6 @@ def can_transfer(self, origin_account):
if CleanText(li.xpath('.//span[@class="_c1 doux _c1"]'), replace=[(' ', '')])(self) in origin_account:
return True
@method
class iter_recipients(ListElement):
def parse(self, el):
if self.page.IS_PRO_PAGE:
self.item_xpath = '//ul[@id="idDetailsListCptCrediterVertical:ul"]//ul/li'
else:
self.item_xpath = '//ul[@id="idDetailsListCptCrediterHorizontal:ul"]//li[@role="radio"]'
class item(MyRecipient):
condition = lambda self: Field('id')(self) not in self.env['origin_account'].id
obj_bank_name = 'Crédit Mutuel'
obj_label = CleanText('.//div[@role="presentation"]/em | .//div[not(@id) and not(@role)]')
obj_id = CleanText('.//span[@class="_c1 doux _c1"]', replace=[(' ', '')])
obj_category = 'Interne'
def obj_iban(self):
l = [a for a in self.page.browser.get_accounts_list()
if Field('id')(self) in a.id and empty(a.valuation_diff)]
assert len(l) == 1
return l[0].iban
def get_account_index(self, direction, account):
for div in self.doc.xpath('//*[has-class("dw_dli_contents")]'):
......@@ -2127,8 +2115,13 @@ def get_to_account_index(self, account):
return self.get_account_index(self.RECIPIENT_STRING, account)
def get_transfer_form(self):
# internal and external transfer form are differents
return self.get_form(id='P:F', submit='//input[@type="submit" and contains(@value, "Valider")]')
# internal and external transfer forms are differents ("P:F" vs "P2:F")
# but also the form id is sometimes changed from
# "P1:F" to "P2:F" and from "P2:F" to "P3:F"
# search for other info to get transfer form
transfer_form_xpath = '//form[contains(@action, "fr/banque/virements/vplw") and @method="post"]'
transfer_form_submit_xpath = '//input[@type="submit" and contains(@value, "Valider")]'
return self.get_form(xpath=transfer_form_xpath, submit=transfer_form_submit_xpath)
def prepare_transfer(self, account, to, amount, reason, exec_date):
form = self.get_transfer_form()
......@@ -2201,7 +2194,19 @@ def get_transfer_webid(self):
parsed = urlparse(self.url)
return parse_qs(parsed.query)['_saguid'][0]
def handle_response(self, account, recipient, amount, reason, exec_date):
def handle_response_reuse_transfer(self, transfer):
self.check_errors()
exec_date, r_amount, currency = self.check_data_consistency(
transfer.account_id, transfer.recipient_id, transfer.amount, transfer.label)
transfer.exec_date = exec_date
transfer.amount = r_amount
transfer.currency = currency
return transfer
def handle_response_create_transfer(self, account, recipient, amount, reason, exec_date):
self.check_errors()
self.check_success()
......@@ -2258,7 +2263,35 @@ class iter_emitters(ListEmitters):
pass
class ExternalTransferPage(InternalTransferPage):
class InternalTransferPage(TransferPageCommon):
RECIPIENT_STRING = 'data_input_indiceCompteACrediter'
READY_FOR_TRANSFER_MSG = 'Confirmer un virement entre vos comptes'
SUMMARY_RECIPIENT_TITLE = 'Compte à créditer'
@method
class iter_recipients(ListElement):
def parse(self, el):
if self.page.IS_PRO_PAGE:
self.item_xpath = '//ul[@id="idDetailsListCptCrediterVertical:ul"]//ul/li'
else:
self.item_xpath = '//ul[@id="idDetailsListCptCrediterHorizontal:ul"]//li[@role="radio"]'
class item(MyRecipient):
condition = lambda self: Field('id')(self) not in self.env['origin_account'].id
obj_bank_name = 'Crédit Mutuel'
obj_label = CleanText('.//div[@role="presentation"]/em | .//div[not(@id) and not(@role)]')
obj_id = CleanText('.//span[@class="_c1 doux _c1"]', replace=[(' ', '')])
obj_category = 'Interne'
def obj_iban(self):
l = [a for a in self.page.browser.get_accounts_list()
if Field('id')(self) in a.id and empty(a.valuation_diff)]
assert len(l) == 1
return l[0].iban
class ExternalTransferPage(TransferPageCommon):
RECIPIENT_STRING = 'data_input_indiceBeneficiaire'
READY_FOR_TRANSFER_MSG = 'Confirmer un virement vers un bénéficiaire enregistré'
SUMMARY_RECIPIENT_TITLE = 'Bénéficiaire à créditer'
......@@ -2316,17 +2349,6 @@ def obj_iban(self):
def parse(self, el):
self.env['origin_account']._external_recipients.add(Field('id')(self))
def get_transfer_form(self):
# transfer form id change from "P1:F" to "P2:F" and from "P2:F" to "P3:F"
# search for other info to get transfer form
transfer_form_xpath = '//form[contains(@action, "fr/banque/virements/vplw") and @method="post"]'
transfer_form_submit_xpath = '//input[@type="submit" and contains(@value, "Valider")]'
return self.get_form(xpath=transfer_form_xpath, submit=transfer_form_submit_xpath)
@method
class iter_emitters(ListEmitters):
pass
class VerifCodePage(LoggedPage, HTMLPage):
HASHES = {
......@@ -2496,7 +2518,12 @@ def go_to_add(self):
form.submit()
def get_add_recipient_form(self, recipient):
form = self.get_form(id='P:F')
# form id change from "P:F" to "P2:F" and from "P2:F" to "P3:F"
# search for other info to get transfer form
rcpt_form_xpath = '//form[contains(@action, "fr/banque/virements/vplw") and @method="post"]'
rcpt_form_submit_xpath = '//input[@type="submit" and contains(@value, "Valider")]'
form = self.get_form(xpath=rcpt_form_xpath, submit=rcpt_form_submit_xpath)
del form['_FID_GoI%5fRechercheBIC']
form['[t:dbt%3astring;x(70)]data_input_nom'] = recipient.label
form['[t:dbt%3astring;x(34)]data_input_IBANBBAN'] = recipient.iban
......@@ -2622,6 +2649,9 @@ class RevolvingLoanDetails(LoggedPage, HTMLPage):
class SubscriptionPage(LoggedPage, HTMLPage):
def error_msg(self):
return CleanText('//div[@id="errmsg"]/p')(self.doc)
def get_link_to_bank_statements(self):
return Link('//a[@id="C:R1:N"]')(self.doc)
......
# -*- coding: utf-8 -*-
# Copyright(C) 2019 Guntra
#
# This file is part of a woob module.
#
# This woob module is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This woob module 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this woob module. If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from .module import LesterrainsModule
__all__ = ['LesterrainsModule']
# -*- coding: utf-8 -*-
# Copyright(C) 2019 Guntra
#
# This file is part of a woob module.
#
# This woob module is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This woob module 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this woob module. If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from woob.browser import PagesBrowser, URL
from woob.capabilities.housing import POSTS_TYPES, HOUSE_TYPES
from woob.tools.compat import urlencode
from .pages import CitiesPage, SearchPage, HousingPage
class LesterrainsBrowser(PagesBrowser):
BASEURL = 'http://www.les-terrains.com'
TYPES = {
POSTS_TYPES.SALE: 'vente'
}
RET = {
HOUSE_TYPES.LAND: 'Terrain seul'
}
cities = URL(r'/api/get-search.php\?q=(?P<city>.*)', CitiesPage)
search = URL(r'/index.php\?mode_aff=liste&ongletAccueil=Terrains&(?P<query>.*)&distance=0', SearchPage)
housing = URL(
r'/index.php\?page=terrains&mode_aff=un_terrain&idter=(?P<_id>\d+).*',
r'/index.php\?page=terrains&mode_aff=maisonterrain&idter=(?P<_id>\d+).*',
HousingPage
)
def get_cities(self, pattern):
return self.cities.open(city=pattern).get_cities()
def search_housings(self, cities, area_min, area_max, cost_min, cost_max):
def _get_departement(city):
return city.split(';')[0][:2]
def _get_ville(city):
return city.split(';')[1]
for city in cities:
query = urlencode({
"departement": _get_departement(city),
"ville": _get_ville(city),
"prixMin": cost_min or '',
"prixMax": cost_max or '',
"surfMin": area_min or '',
"surfMax": area_max or '',
})
for house in self.search.go(query=query).iter_housings():
yield house
def get_housing(self, _id, housing=None):
return self.housing.go(_id=_id).get_housing(obj=housing)
# -*- coding: utf-8 -*-
# Copyright(C) 2019 Guntra
#
# This file is part of a woob module.
#
# This woob module is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This woob module 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this woob module. If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from woob.tools.backend import Module
from woob.capabilities.housing import CapHousing
from .browser import LesterrainsBrowser
# Some remarks:
# - post type is hardcoded as POSTS_TYPES.SALE because it makes sense here to have it fixed
# - advert is hardcoded as ADVERT_TYPES.PROFESSIONAL (same)
# - house type is hardcoded as HOUSE_TYPES.LAND (same)
# - Only the first city in the query is taken into account for now (work in progress)
# - If a post has multiple lands, we choose the lowest cost and the highest area to have the best match.
# You'll have to review manually the lands of course and see if there is a good combo cost/area.
# So don't be too happy if you see a cheap big land ;)
__all__ = ['LesterrainsModule']
class LesterrainsModule(Module, CapHousing):
NAME = 'lesterrains'
DESCRIPTION = 'Les-Terrains.com'
MAINTAINER = 'Guntra'
EMAIL = 'guntra@example.com'
LICENSE = 'LGPLv3+'
VERSION = '3.0'
BROWSER = LesterrainsBrowser
def search_city(self, pattern):
return self.browser.get_cities(pattern)
def search_housings(self, query):
cities = ['%s' % c.id for c in query.cities if c.backend == self.name]
if len(cities) == 0:
return list()
return self.browser.search_housings(
cities,
query.area_min,
query.area_max,
query.cost_min,
query.cost_max
)
def get_housing(self, housing):
return self.browser.get_housing(housing)
# -*- coding: utf-8 -*-
# Copyright(C) 2019 Guntra
#
# This file is part of a woob module.
#
# This woob module is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This woob module 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this woob module. If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from woob.browser.filters.standard import (
CleanDecimal, CleanText,
Date, Lower, Regexp, QueryValue,
)
from woob.browser.filters.json import Dict
from woob.browser.filters.html import Attr, AbsoluteLink
from woob.browser.elements import ItemElement, ListElement, DictElement, method
from woob.browser.pages import JsonPage, HTMLPage, pagination
from woob.capabilities.base import Currency, NotAvailable
from woob.capabilities.housing import (
Housing, HousingPhoto, City,
POSTS_TYPES, HOUSE_TYPES, ADVERT_TYPES, UTILITIES
)
class CitiesPage(JsonPage):
ENCODING = 'UTF-8'
def build_doc(self, content):
content = super(CitiesPage, self).build_doc(content)