Commit dfa4af19 authored by Phyks (Lucas Verney)'s avatar Phyks (Lucas Verney) Committed by Vincent A

Start to implement FURNISHED_RENT type for housings

parent 7d3c3d30
......@@ -35,7 +35,8 @@ class ExplorimmoBrowser(PagesBrowser):
'rest/classifieds/\?(?P<js_datas>.*)', HousingPage2)
TYPES = {POSTS_TYPES.RENT: 'location',
POSTS_TYPES.SALE: 'vente'}
POSTS_TYPES.SALE: 'vente',
POSTS_TYPES.FURNISHED_RENT: 'location'}
RET = {HOUSE_TYPES.HOUSE: 'Maison',
HOUSE_TYPES.APART: 'Appartement',
......@@ -58,6 +59,7 @@ class ExplorimmoBrowser(PagesBrowser):
ret.append(self.RET.get(house_type))
data = {'location': ','.join(cities).encode('iso 8859-1'),
'furnished': type == POSTS_TYPES.FURNISHED_RENT,
'areaMin': area_min or '',
'areaMax': area_max or '',
'priceMin': cost_min or '',
......
......@@ -193,14 +193,16 @@ class HousingPage2(JsonPage):
klass = Housing
def is_agency(self):
return 'un particulier' in CleanText(
'.//div[has-class("container-agency-infos")]')(self).lower()
return Dict('agency/isParticulier')(self) == 'false'
obj_id = Env('_id')
def obj_type(self):
transaction = Dict('characteristics/transaction')(self)
if transaction == 'location':
return POSTS_TYPES.RENT
if Dict('characteristics/isFurnished')(self) == 'true':
return POSTS_TYPES.FURNISHED_RENT
else:
return POSTS_TYPES.RENT
elif transaction == 'vente':
return POSTS_TYPES.SALE
else:
......
......@@ -2,21 +2,23 @@ from weboob.capabilities.housing import POSTS_TYPES, HOUSE_TYPES
QUERY_TYPES = {
POSTS_TYPES.RENT: 'location',
POSTS_TYPES.SALE: 'achat'
POSTS_TYPES.SALE: 'achat',
POSTS_TYPES.FURNISHED_RENT: 'location'
}
QUERY_HOUSE_TYPES = {
HOUSE_TYPES.APART: ['appartement'],
HOUSE_TYPES.APART: ['appartement', 'appartement-meuble'],
HOUSE_TYPES.HOUSE: ['maison'],
HOUSE_TYPES.PARKING: ['parking'],
HOUSE_TYPES.LAND: ['terrain'],
HOUSE_TYPES.OTHER: ['chambre', 'appartement-meuble',
HOUSE_TYPES.OTHER: ['chambre',
'local-commercial', 'immeuble']
}
AVAILABLE_TYPES = {
POSTS_TYPES.RENT: ['appartement', 'maison', 'parking', 'chambre',
'appartement-meuble', 'local-commercial'],
'local-commercial'],
POSTS_TYPES.SALE: ['appartement', 'maison', 'parking', 'local-commercial',
'terrain', 'immeuble']
'terrain', 'immeuble'],
POSTS_TYPES.FURNISHED_RENT: ['appartement-meuble']
}
......@@ -63,7 +63,10 @@ class HousingPage(HTMLPage):
def obj_type(self):
type = Env('type')(self)
if type == 'location':
return POSTS_TYPES.RENT
if 'appartement-meuble' in self.page.url:
return POSTS_TYPES.FURNISHED_RENT
else:
return POSTS_TYPES.RENT
elif type == 'achat':
return POSTS_TYPES.SALE
else:
......
......@@ -28,11 +28,11 @@ class LeboncoinBrowser(PagesBrowser):
city = URL('ajax/location_list.html\?city=(?P<city>.*)&zipcode=(?P<zip>.*)', CityListPage)
search = URL('(?P<type>.*)/offres/\?(?P<_ps>ps|mrs)=(?P<ps>.*)&(?P<_pe>pe|mre)=(?P<pe>.*)&ros=(?P<ros>.*)&location=(?P<location>.*)&sqs=(?P<sqs>.*)&sqe=(?P<sqe>.*)&ret=(?P<ret>.*)&f=(?P<advert_type>.*)',
'(?P<_type>.*)/offres/occasions.*?',
HousingListPage)
housing = URL('ventes_immobilieres/(?P<_id>.*).htm', HousingPage)
phone = URL('https://api.leboncoin.fr/api/utils/phonenumber.json', PhonePage)
TYPES = {POSTS_TYPES.RENT: 'locations',
POSTS_TYPES.FURNISHED_RENT: 'locations',
POSTS_TYPES.SALE: 'ventes_immobilieres',
POSTS_TYPES.SHARING: 'colocations', }
......@@ -59,7 +59,7 @@ class LeboncoinBrowser(PagesBrowser):
if query.type not in self.TYPES:
return TypeNotSupported()
type, cities, nb_rooms, area_min, area_max, cost_min, cost_max, ret = self.decode_query(query, module_name)
type, cities, nb_rooms, area_min, area_max, cost_min, cost_max, ret, furn = self.decode_query(query, module_name)
if len(cities) == 0 or len(ret) == 0:
return list()
......@@ -67,6 +67,7 @@ class LeboncoinBrowser(PagesBrowser):
ros=nb_rooms,
sqs=area_min,
sqe=area_max,
furn=furn,
_ps="mrs" if query.type == POSTS_TYPES.RENT else "ps",
ps=cost_min,
_pe="mre" if query.type == POSTS_TYPES.RENT else "pe",
......@@ -100,4 +101,11 @@ class LeboncoinBrowser(PagesBrowser):
cost_min = '' if not query.cost_min else self.page.get_cost_min(query.cost_min, query.type)
cost_max = '' if not query.cost_max else self.page.get_cost_max(query.cost_max, query.type)
return _type, ','.join(cities), nb_rooms, area_min, area_max, cost_min, cost_max, '&ret='.join(ret)
if query.type == POSTS_TYPES.FURNISHED_RENT:
furn = '1'
elif query.type == POSTS_TYPES.RENT:
furn = '2'
else:
furn = ''
return _type, ','.join(cities), nb_rooms, area_min, area_max, cost_min, cost_max, '&ret='.join(ret), furn
......@@ -16,6 +16,7 @@
#
# 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.pages import HTMLPage, pagination, JsonPage
from weboob.browser.elements import ItemElement, ListElement, method
......@@ -221,6 +222,9 @@ class HousingPage(HTMLPage):
self.env['typeBien'] = HOUSE_TYPES.LAND
else:
self.env['typeBien'] = HOUSE_TYPES.OTHER
elif 'Meublé' in property:
value = CleanText('./span[@class="value"]')(item).lower()
self.env['isFurnished'] = (value == 'meublé')
else:
key = u'%s' % CleanText('./span[@class="property"]')(item)
if 'GES' in key or 'Classe' in key:
......@@ -244,7 +248,10 @@ class HousingPage(HTMLPage):
if 'colocations' in breadcrumb:
return POSTS_TYPES.SHARING
elif 'locations' in breadcrumb:
return POSTS_TYPES.RENT
if self.env['isFurnished']:
return POSTS_TYPES.FURNISHED_RENT
else:
return POSTS_TYPES.RENT
else:
return POSTS_TYPES.SALE
def obj_advert_type(self):
......
......@@ -35,7 +35,8 @@ class LogicimmoBrowser(PagesBrowser):
TYPES = {POSTS_TYPES.RENT: 'location-immobilier',
POSTS_TYPES.SALE: 'vente-immobilier',
POSTS_TYPES.SHARING: 'recherche-colocation'}
POSTS_TYPES.SHARING: 'recherche-colocation',
POSTS_TYPES.FURNISHED_RENT: 'location-immobilier'}
RET = {HOUSE_TYPES.HOUSE: '2',
HOUSE_TYPES.APART: '1',
......@@ -48,7 +49,6 @@ class LogicimmoBrowser(PagesBrowser):
return self.city.go(pattern=pattern).get_cities()
def search_housings(self, type, cities, nb_rooms, area_min, area_max, cost_min, cost_max, house_types):
if type not in self.TYPES:
raise TypeNotSupported()
......@@ -62,6 +62,9 @@ class LogicimmoBrowser(PagesBrowser):
if len(ret):
options.append('groupprptypesids=%s' % ','.join(ret))
if type == POSTS_TYPES.FURNISHED_RENT:
options.append('searchoptions=4')
options.append('pricemin=%s' % (cost_min if cost_min else '0'))
if cost_max:
......
......@@ -64,7 +64,17 @@ class HousingPage(HTMLPage):
if 'colocation' in url:
return POSTS_TYPES.SHARING
elif 'location' in url:
return POSTS_TYPES.RENT
isFurnished = False
for li in XPath('//ul[@itemprop="description"]/li')(self):
label = CleanText('./div[has-class("criteria-label")]')(li)
if label.lower() == "meublé":
isFurnished = (
CleanText('./div[has-class("criteria-value")]')(li).lower() == 'oui'
)
if isFurnished:
return POSTS_TYPES.FURNISHED_RENT
else:
return POSTS_TYPES.RENT
elif 'vente' in url:
return POSTS_TYPES.SALE
return NotAvailable
......
......@@ -19,7 +19,7 @@
from weboob.browser import PagesBrowser, URL
from weboob.capabilities.housing import TypeNotSupported
from weboob.capabilities.housing import TypeNotSupported, POSTS_TYPES
from weboob.tools.compat import urlencode
from .pages import SearchResultsPage, HousingPage, CitiesPage
......@@ -60,6 +60,9 @@ class PapBrowser(PagesBrowser):
data['nb_pieces[min]'] = nb_rooms
data['nb_pieces[max]'] = nb_rooms
if type == POSTS_TYPES.FURNISHED_RENT:
data['tags[]'] = 'meuble'
ret = []
for house_type in house_types:
if house_type in RET:
......
from weboob.capabilities.housing import POSTS_TYPES, HOUSE_TYPES
TYPES = {POSTS_TYPES.RENT: 'location',
POSTS_TYPES.FURNISHED_RENT: 'location',
POSTS_TYPES.SALE: 'vente'}
RET = {HOUSE_TYPES.HOUSE: 'maison',
......
......@@ -16,7 +16,7 @@
#
# 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 decimal import Decimal
from weboob.tools.date import parse_french_date
......@@ -29,7 +29,7 @@ from weboob.browser.filters.json import Dict
from weboob.capabilities.base import NotAvailable
from weboob.capabilities.housing import (Housing, City, HousingPhoto,
UTILITIES, ENERGY_CLASS, POSTS_TYPES,
ADVERT_TYPES, HOUSE_TYPES)
ADVERT_TYPES, HOUSE_TYPES, POSTS_TYPES)
from weboob.tools.capabilities.housing.housing import PricePerMeterFilter
......@@ -57,12 +57,31 @@ class SearchResultsPage(HTMLPage):
klass = Housing
def condition(self):
return Regexp(Link('./div[has-class("box-header")]/a[@class="title-item"]'), '/annonces/(.*)', default=None)(self)
title = CleanText('./div[has-class("box-header")]/a[@class="title-item"]')(self)
isNotFurnishedOk = True
if self.env['query_type'] == POSTS_TYPES.RENT:
isNotFurnishedOk = 'meublé' not in title.lower()
return (
Regexp(Link('./div[has-class("box-header")]/a[@class="title-item"]'), '/annonces/(.*)', default=None)(self)
and isNotFurnishedOk
)
obj_id = Regexp(Link('./div[has-class("box-header")]/a[@class="title-item"]'), '/annonces/(.*)')
obj_type = Env('query_type')
obj_advert_type = ADVERT_TYPES.PERSONAL
obj_house_type = NotAvailable
def obj_house_type(self):
item_link = Link('.//a[has-class("title-item")]')(self)
house_type = item_link.split('/')[-1].split('-')[0]
if 'parking' in house_type:
return HOUSE_TYPES.PARKING
elif 'appartement' in house_type:
return HOUSE_TYPES.APART
elif 'terrain' in house_type:
return HOUSE_TYPES.LAND
elif 'maison' in house_type:
return HOUSE_TYPES.HOUSE
else:
return HOUSE_TYPES.OTHER
obj_title = CleanText('./div[has-class("box-header")]/a[@class="title-item"]')
......@@ -131,7 +150,13 @@ class HousingPage(HTMLPage):
def obj_type(self):
prev_link = Link('//ol[has-class("breadcrumb")]/li[1]/a')(self)
if 'location' in prev_link:
return POSTS_TYPES.RENT
title = CleanText(
'//div[has-class("box-header")]/h1[@class="clearfix"]'
)(self)
if 'meublé' in title.lower():
return POSTS_TYPES.FURNISHED_RENT
else:
return POSTS_TYPES.RENT
elif 'vente' in prev_link:
return POSTS_TYPES.SALE
else:
......
from weboob.capabilities.housing import POSTS_TYPES, HOUSE_TYPES
TYPES = {POSTS_TYPES.RENT: 1,
POSTS_TYPES.SALE: 2}
POSTS_TYPES.SALE: 2,
POSTS_TYPES.FURNISHED_RENT: 1}
RET = {HOUSE_TYPES.HOUSE: '2',
HOUSE_TYPES.APART: '1',
......
......@@ -26,7 +26,7 @@ from weboob.browser.filters.standard import (CleanText, CleanDecimal, Currency,
DateTime, Format, Regexp)
from weboob.capabilities.base import NotAvailable
from weboob.capabilities.housing import (Housing, HousingPhoto, City,
UTILITIES, ENERGY_CLASS)
UTILITIES, ENERGY_CLASS, POSTS_TYPES)
from weboob.tools.capabilities.housing.housing import PricePerMeterFilter
from .constants import TYPES, RET
......@@ -58,7 +58,12 @@ class SeLogerItem(ItemElement):
def obj_type(self):
idType = int(CleanText('idTypeTransaction')(self))
return next(k for k, v in TYPES.items() if v == idType)
type = next(k for k, v in TYPES.items() if v == idType)
if type == POSTS_TYPES.FURNISHED_RENT:
# SeLoger does not let us discriminate between furnished and not
# furnished.
return POSTS_TYPES.RENT
return type
obj_advert_type = NotAvailable
def obj_house_type(self):
idType = CleanText('idTypeBien')(self)
......
......@@ -174,23 +174,16 @@ class Flatboob(ReplApplication):
query.house_types.append(value)
_type = None
house_types = [POSTS_TYPES.RENT, POSTS_TYPES.SALE, POSTS_TYPES.SHARING]
while _type not in [0, 1, 2]:
print(' %s%2d)%s %s' % (self.BOLD,
0,
self.NC,
"Rent"))
print(' %s%2d)%s %s' % (self.BOLD,
1,
self.NC,
"Sale"))
print(' %s%2d)%s %s' % (self.BOLD,
2,
self.NC,
"Sharing"))
posts_types = sorted(POSTS_TYPES.values)
while _type not in range(len(posts_types)):
for i, t in enumerate(posts_types):
print(' %s%2d)%s %s' % (self.BOLD,
i,
self.NC,
t))
_type = self.ask_int('Type of query')
query.type = house_types[_type]
query.type = posts_types[_type]
r = 'notempty'
while r != '':
......
......@@ -60,7 +60,8 @@ ENERGY_CLASS = enum(A=u'A', B=u'B', C=u'C', D=u'D', E=u'E', F=u'F', G=u'G')
POSTS_TYPES = enum(RENT=u'RENT',
SALE=u'SALE',
SHARING=u'SHARING')
SHARING=u'SHARING',
FURNISHED_RENT=u'FURNISHED_RENT')
ADVERT_TYPES = enum(PROFESSIONAL=u'Professional', PERSONAL=u'Personal')
HOUSE_TYPES = enum(APART=u'Apartment',
HOUSE=u'House',
......@@ -78,7 +79,7 @@ class Housing(BaseObject):
*POSTS_TYPES.types)
advert_type = Field('Type of advert (professional or personal)',
*ADVERT_TYPES.types)
house_type = Field('Type of house (apartment, house, parking, …)',
house_type = Field(u'Type of house (apartment, house, parking, …)',
*HOUSE_TYPES.types)
title = StringField('Title of housing')
area = DecimalField('Area of housing, in m2')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment