def obj_house_type(self): item_link = Link('./div/a[@class="item-title"]')(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
def obj_house_type(self): prev_link = Link('//ol[has-class("breadcrumb")]/li[1]/a')(self) house_type = prev_link.split('-')[-1] 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
def get_panel_link(self): return Link( '//a[contains(@href, "homepage.html") and has-class(@nav-link)]')( self.doc)
def obj__life_investments(self): xpath_link = '//li[contains(., "{acc_number}")]/ul/li/a[contains(text(), "Solde")]'.format( acc_number=Field('id')(self)) return Link(xpath_link)(self)
def load_iban(self): link = Link(TableCell('rib')(self)[0].xpath( './a[img[starts-with(@alt, "RIB")]]'), default=None)(self) return self.page.browser.async_open(link)
def get_me(self): return Regexp(Link('//a[@data-nav="view_profile"]'), '/(.+)')(self.doc)
def load_details(self): url = Link('.//a', default=NotAvailable)(self) return self.page.browser.async_open(url=url)
def next_page(self): next_page = Link(u'//a[contains(text(), "précédentes")]', default=None)(self) if next_page: return "/%s" % next_page
def come_back(self): link = Link(u'//div/a[contains(text(), "Accueil accès client")]', default=NotAvailable)(self.doc) if link: self.browser.location(link)
def obj__has_cards(self): return Link(u'.//a[contains(., "Débit différé")]', default=None)(self)
def get_patrimoine_url(self): return Link('//a[contains(., "Espace Patrimoine")]', default=None)(self.doc)
def get_link(self, page): return Link(default=NotAvailable).filter( self.doc.xpath('//a[contains(text(), "%s")]' % page))
def obj_url(self): return urljoin(self.page.url, Link('.//a')(self))
def get_ibanlink(self): return Link('//a[@id="editionRibLevel2link"]', default=None)(self.doc)
def obj_investments(self): investments_popup = self.page.get_transaction_investments_popup(Regexp(Link('.//a'), r"\'(.*?)\'")(self)) # iter from investments_popup to get transaction investments return [inv for inv in investments_popup.page.iter_transaction_investments(investments=Env('investments')(self))]
class item(ItemElement): klass = Account def condition(self): return len(self.el.xpath('./td')) > 2 class Label(Filter): def filter(self, text): return text.lstrip(' 0123456789').title() class Type(Filter): PATTERNS = [ ('invest', Account.TYPE_MARKET), ('ldd', Account.TYPE_SAVINGS), ('livret', Account.TYPE_SAVINGS), ('compte', Account.TYPE_CHECKING), ('account', Account.TYPE_CHECKING), ('pret', Account.TYPE_LOAN), ('vie', Account.TYPE_LIFE_INSURANCE), ('strategie patr.', Account.TYPE_LIFE_INSURANCE), ('essentiel', Account.TYPE_LIFE_INSURANCE), ('elysee', Account.TYPE_LIFE_INSURANCE), ('abondance', Account.TYPE_LIFE_INSURANCE), ('ely. retraite', Account.TYPE_LIFE_INSURANCE), ('lae option assurance', Account.TYPE_LIFE_INSURANCE), ('carte ', Account.TYPE_CARD), ] def filter(self, label): label = label.lower() for pattern, type in self.PATTERNS: if pattern in label: return type return Account.TYPE_UNKNOWN obj_label = Label(CleanText('./td[1]/a')) obj_coming = Env('coming') obj_currency = FrenchTransaction.Currency('./td[2]') obj__link_id = Link('./td[1]/a') obj_type = Type(Field('label')) obj_coming = NotAvailable @property def obj_balance(self): if self.el.xpath('./parent::*/tr/th') and self.el.xpath( './parent::*/tr/th')[0].text in [ u'Credits', u'Crédits' ]: return CleanDecimal(replace_dots=True, sign=lambda x: -1).filter( self.el.xpath('./td[3]')) return CleanDecimal(replace_dots=True).filter( self.el.xpath('./td[3]')) @property def obj_id(self): # Investment account and main account can have the same id # so we had account type in case of Investment to prevent conflict if Field('type')(self) == Account.TYPE_MARKET: return CleanText(replace=[('.', ''), (' ', '')]).filter( self.el.xpath('./td[2]')) + ".INVEST" return CleanText(replace=[('.', ''), (' ', '')]).filter( self.el.xpath('./td[2]'))
def next_page(self): return Link('//a[@title="Suivant"]', default=None)(self)
class item(ItemElement): klass = Investment obj_label = CleanText(TableCell('label')) obj_code = QueryValue(Link('.//a[contains(@href, "isin")]', default=''), 'isin', default=NotAvailable) def valuation(self): td = TableCell('valuation')(self)[0] return CleanDecimal('.')(td) def obj_quantity(self): if not self.page.is_detail(): return NotAvailable td = TableCell('quantity')(self)[0] return CleanDecimal('.//span[1]', replace_dots=True)(td) def obj_valuation(self): if self.obj_original_currency(): return NotAvailable return self.valuation() def obj_original_valuation(self): if self.obj_original_currency(): return self.valuation() return NotLoaded def obj_vdate(self): td = TableCell('vdate')(self)[0] txt = CleanText('./text()')(td) return Date('.', dayfirst=True, default=NotAvailable).filter(txt) def obj_code_type(self): lst = self.el.xpath('./th/a') if not lst: return NotAvailable return Investment.CODE_TYPE_ISIN obj_code = Regexp(Link('./th/a', default=''), r'isin=(.{12})$', default=NotAvailable) def unitvalue(self): return CleanDecimal(TableCell('unitvalue'), replace_dots=True)(self) def obj_unitvalue(self): if not self.page.is_detail() or self.obj_original_currency(): return NotAvailable return self.unitvalue() def obj_original_unitvalue(self): if self.page.is_detail() and self.obj_original_currency(): return self.unitvalue() return NotLoaded def obj_portfolio_share(self): if self.page.is_detail(): return NotAvailable return Eval( lambda x: x / 100, CleanDecimal(TableCell('portfolio_share'), replace_dots=True))(self) def obj_original_currency(self): cur = Currency(TableCell('valuation'))(self) return cur if self.env['currency'] != cur else NotLoaded
def obj_url(self): url = Link(u'./a', default=NotAvailable)(self) if url: return urljoin(self.page.url, url) return url
def mandate_management_space_link(self): return Link(u'//a[@title="Accéder aux Comptes Gérés Sous Mandat"]')(self.doc)
def obj__link(self): return Link(TableCell('id')(self)[0].xpath('./a'), default=None)(self)
def obj_url(self): url = Link(u'.//a', default=None)(self) if url: return urljoin(self.page.url, url) return self.page.url
def obj_url(self): acc_number = Field('id')(self) xpath_link = '//li[contains(., "{acc_number}")]/ul/li/a'.format( acc_number=acc_number) return Link(xpath_link)(self)
class item(ItemElement): klass = Document obj_id = Format('%s_%s%s', Env('sub_id'), Regexp(CleanText('.//a/@title'), r' (\d{2}) '), CleanText('.//span[contains(@class, "date")]' ,symbols='/')) obj_label = Format('%s - %s', CleanText('.//span[contains(@class, "lib")]'), CleanText('.//span[contains(@class, "date")]')) obj_url = Format('/voscomptes/canalXHTML/relevePdf/relevePdf_historique/%s', Link('./a')) obj_format = 'pdf' obj_type = DocumentTypes.OTHER def obj_date(self): date = CleanText('.//span[contains(@class, "date")]')(self) m = re.search(r'(\d{2}/\d{2}/\d{4})', date) if m: return Date(CleanText('.//span[contains(@class, "date")]'), dayfirst=True)(self) else: return Date( Format( '%s/%s', Regexp(CleanText('.//a/@title'), r' (\d{2}) '), CleanText('.//span[contains(@class, "date")]') ), dayfirst=True )(self)
def obj_url(self): td = TableCell('id')(self)[0] return Link(td.xpath('./a'))(self)
def obj__url(self): return Link(TableCell('rib')(self)[0].xpath( './a[img[starts-with(@alt, "RIB")]]'), default=None)(self)
class iter_housings(ListElement): item_xpath = '//article[has-class("TeaserOffer")]' next_page = Link( '//div[has-class("Pagination--more")]/a[contains(text(), "Suivant")]' ) class item(ItemElement): klass = Housing obj_id = Format( '%s:%s', Env('type'), Attr('.//span[boolean(@data-reference)]', 'data-reference')) obj_url = AbsoluteLink('.//h3[has-class("TeaserOffer-title")]/a') obj_type = Env('query_type') obj_advert_type = ADVERT_TYPES.PROFESSIONAL def obj_house_type(self): url = self.obj_url(self) for house_type, types in QUERY_HOUSE_TYPES.items(): for type in types: if ('/%s/' % type) in url: return house_type return NotAvailable obj_url = AbsoluteLink('.//h3[has-class("TeaserOffer-title")]/a') obj_title = CleanText('.//h3[has-class("TeaserOffer-title")]') obj_area = CleanDecimal( Regexp( CleanText( './/div[has-class("MiniData")]//p[@data-behat="surfaceDesBiens"]' ), r'(\d*\.*\d*) .*')) obj_cost = CleanDecimal( './/strong[has-class("TeaserOffer-price-num")]') obj_price_per_meter = PricePerMeterFilter() obj_currency = Currency( './/strong[has-class("TeaserOffer-price-num")]') obj_location = CleanText('.//p[has-class("TeaserOffer-loc")]') obj_text = CleanText('.//p[has-class("TeaserOffer-description")]') def obj_photos(self): url = CleanText( Attr('.//a[has-class("TeaserOffer-ill")]/img', 'src'))(self) # If the used photo is a default no photo, the src is on the same domain. if url[0] == '/': return [] else: return [HousingPhoto(url)] obj_date = datetime.date.today() def obj_utilities(self): price = CleanText( './/strong[has-class("TeaserOffer-price-num")]')(self) if "charges comprises" in price.lower(): return UTILITIES.INCLUDED else: return UTILITIES.EXCLUDED obj_rooms = CleanDecimal( './/div[has-class("MiniData")]//p[@data-behat="nbPiecesDesBiens"]', default=NotAvailable) obj_bedrooms = CleanDecimal( './/div[has-class("MiniData")]//p[@data-behat="nbChambresDesBiens"]', default=NotAvailable) def obj_details(self): return { "dispo": Date( Attr('.//span[boolean(@data-dispo)]', 'data-dispo', default=datetime.date.today().isoformat()))(self), "priceMentions": CleanText( './/span[has-class("TeaserOffer-price-mentions")]')( self) }
def get_acc_link(self): msg = CleanText('//body[@class="message"]')(self.doc) if msg: acc_link = Link('//div[@class="Boutons"]/a', 'href')(self.doc) return acc_link
class get_housing(ItemElement): klass = Housing obj_id = Format( '%s:%s', Env('type'), Attr('//div[boolean(@data-property-reference)]', 'data-property-reference')) obj_advert_type = ADVERT_TYPES.PROFESSIONAL def obj_type(self): type = Env('type')(self) if type == 'location': 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: return NotAvailable def obj_url(self): return self.page.url def obj_house_type(self): url = self.obj_url() for house_type, types in QUERY_HOUSE_TYPES.items(): for type in types: if ('/%s/' % type) in url: return house_type return NotAvailable obj_title = CleanText('//h1[has-class("OfferTop-title")]') obj_area = CleanDecimal( Regexp( CleanText( '//div[has-class("MiniData")]//p[has-class("MiniData-item")][1]' ), r'(\d*\.*\d*) .*')) obj_cost = CleanDecimal('//p[has-class("OfferTop-price")]') obj_price_per_meter = PricePerMeterFilter() obj_currency = Currency('//p[has-class("OfferTop-price")]') obj_location = Format('%s - %s', CleanText('//p[@data-behat="adresseBien"]'), CleanText('//p[has-class("OfferTop-loc")]')) obj_text = CleanText('//div[has-class("OfferDetails-content")]/p[1]') obj_phone = Regexp(Link('//a[has-class("OfferContact-btn--tel")]'), r'tel:(.*)') def obj_photos(self): photos = [] for photo in self.xpath('//div[has-class("OfferSlider")]//img'): photo_url = Attr('.', 'src')(photo) photo_url = photo_url.replace('640/480', '800/600') photos.append(HousingPhoto(photo_url)) return photos obj_date = datetime.date.today() def obj_utilities(self): price = CleanText('//p[has-class("OfferTop-price")]')(self) if "charges comprises" in price.lower(): return UTILITIES.INCLUDED else: return UTILITIES.EXCLUDED obj_rooms = CleanDecimal( '//div[has-class("MiniData")]//p[has-class("MiniData-item")][2]') obj_bedrooms = CleanDecimal( '//div[has-class("MiniData")]//p[has-class("MiniData-item")][3]', default=NotAvailable) def obj_DPE(self): try: electric_consumption = CleanDecimal( Regexp(Attr( '//div[has-class("OfferDetails-content")]//img', 'src'), r'https://dpe.foncia.net\/(\d+)\/.*', default=None))(self) except XPathNotFound: electric_consumption = None DPE = "" if electric_consumption is not None: if electric_consumption <= 50: DPE = "A" elif 50 < electric_consumption <= 90: DPE = "B" elif 90 < electric_consumption <= 150: DPE = "C" elif 150 < electric_consumption <= 230: DPE = "D" elif 230 < electric_consumption <= 330: DPE = "E" elif 330 < electric_consumption <= 450: DPE = "F" else: DPE = "G" return getattr(ENERGY_CLASS, DPE, NotAvailable) def obj_details(self): details = { "dispo": Date( Regexp(CleanText('//p[has-class("OfferTop-dispo")]'), r'.* (\d\d\/\d\d\/\d\d\d\d)', default=datetime.date.today().isoformat()))(self), "priceMentions": CleanText('//p[has-class("OfferTop-mentions")]')(self), "agency": CleanText('//p[has-class("OfferContact-address")]')(self) } for item in self.xpath( '//div[has-class("OfferDetails-columnize")]/div'): category = CleanText( './h3[has-class("OfferDetails-title--2")]')(item) details[category] = {} for detail_item in item.xpath( './/ul[has-class("List--data")]/li'): detail_title = CleanText( './/span[has-class("List-data")]')(detail_item) detail_value = CleanText('.//*[has-class("List-value")]')( detail_item) details[category][detail_title] = detail_value for detail_item in item.xpath( './/ul[has-class("List--bullet")]/li'): detail_title = CleanText('.')(detail_item) details[category][detail_title] = True try: electric_consumption = CleanDecimal( Regexp(Attr( '//div[has-class("OfferDetails-content")]//img', 'src'), r'https://dpe.foncia.net\/(\d+)\/.*', default=None))(self) except XPathNotFound: electric_consumption = None if electric_consumption is not None: details["electric_consumption"] = '{} kWhEP/m².an'.format( electric_consumption) else: details["electric_consumption"] = NotAvailable return details
def next_page(self): return Link('//ul[@class="a-pagination"]/li[@class="a-last"]/a')( self)
def obj_url(self): return Link(TableCell('url')(self)[0].xpath('./a'))(self)
def obj_id(self): link = Link('./a')(self) return link[link.rfind('/') + 1:]
def obj_url(self): acc_number = Field('id')(self) xpath_link = '//li[contains(., "{acc_number}")]/ul/li/a[contains(text(), "Dernieres opérations")]'.format( acc_number=acc_number) return Link(xpath_link)(self)