def get_history(self, date_guesser): seen = set() lines = self.document.xpath('(//table[@class="ca-table"])[2]/tr') for line in lines[1:]: # first line is balance is_balance = line.xpath('./td/@class="cel-texte cel-neg"') [date, label, _, amount] = [self.parser.tocleanstring(td) for td in line.xpath('./td')] t = Transaction(0) t.set_amount(amount) t.label = t.raw = label if is_balance: m = re.search('(\d+ [^ ]+ \d+)', label) if not m: raise BrokenPageError('Unable to read card balance in history: %r' % label) t.date = parse_french_date(m.group(1)) t.amount = -t.amount else: day, month = map(int, date.split('/', 1)) t.date = date_guesser.guess_date(day, month) t.type = t.TYPE_CARD t.rdate = t.date try: t.id = t.unique_id(seen) except UnicodeEncodeError: print t print t.label raise yield t
def get_history(self, date_guesser): seen = set() lines = self.document.xpath('(//table[@class="ca-table"])[2]/tr') for line in lines[1:]: # first line is balance is_balance = line.xpath('./td/@class="cel-texte cel-neg"') [date, label, _, amount ] = [self.parser.tocleanstring(td) for td in line.xpath('./td')] t = Transaction(0) t.set_amount(amount) t.label = t.raw = label if is_balance: m = re.search('(\d+ [^ ]+ \d+)', label) if not m: raise BrokenPageError( 'Unable to read card balance in history: %r' % label) t.date = parse_french_date(m.group(1)) t.amount = -t.amount else: day, month = map(int, date.split('/', 1)) t.date = date_guesser.guess_date(day, month) t.type = t.TYPE_CARD t.rdate = t.date try: t.id = t.unique_id(seen) except UnicodeEncodeError: print t print t.label raise yield t
def parse_transaction(self, transaction): t = FrenchTransaction(transaction['activityId']) date = parse_french_date(transaction['date']) raw = transaction['counterparty'] t.parse(date=date, raw=raw) amount = transaction['displayAmount'] t.set_amount(amount) t._currency = transaction['currencyCode'] return t
def get_history(self): #checking if the card is still valid if self.document.xpath('//div[@id="errorbox"]'): return #adding a time delta because amex have hard time to put the date in a good interval beginning_date = self.get_beginning_debit_date() - datetime.timedelta( days=120) end_date = self.get_end_debit_date() guesser = ChaoticDateGuesser(beginning_date, end_date) for tr in reversed( self.document.xpath( '//div[@id="txnsSection"]//tr[@class="tableStandardText"]') ): cols = tr.findall('td') t = Transaction(tr.attrib['id']) day, month = self.parser.tocleanstring(cols[self.COL_DATE]).split( ' ', 1) day = int(day) month = self.MONTHS.index(month.rstrip('.')) + 1 date = guesser.guess_date(day, month) try: detail = self.parser.select(cols[self.COL_TEXT], 'div.hiddenROC', 1) except BrokenPageError: pass else: detail.drop_tree() raw = (' '.join([ txt.strip() for txt in cols[self.COL_TEXT].itertext() ])).strip() credit = self.parser.tocleanstring(cols[self.COL_CREDIT]) debit = self.parser.tocleanstring(cols[self.COL_DEBIT]) t.date = date t.rdate = date t.raw = re.sub(r'[ ]+', ' ', raw) t.label = re.sub('(.*?)( \d+)? .*', r'\1', raw).strip() t.set_amount(credit, debit) if t.amount > 0: t.type = t.TYPE_ORDER else: t.type = t.TYPE_CARD yield t
def parse(self): for i, tr in enumerate(self.document.xpath('//tr')): t = FrenchTransaction(tr.xpath('./td[@class="transactionId"]/span')[0].text.strip()) date = parse_french_date(tr.xpath('./td[@class="date"]')[0].text.strip()) status = tr.xpath('./td[@class="desc"]/ul/li[@class="first"]')[0].text.strip() #We pass this because it's not transaction if status == u'Créé' or status == u'Annulé': continue raw = tr.xpath('./td[@class="desc"]/strong')[0].text.strip() t.parse(date=date, raw=raw) amount = tr.xpath('./td[@class="price"]/span')[0].text.strip() t.set_amount(amount) t._currency = Account.get_currency(amount) yield t
def get_history(self, date_guesser, state=None): seen = set() lines = self.document.xpath('(//table[@class="ca-table"])[2]/tr') debit_date = None for i, line in enumerate(lines): is_balance = line.xpath('./td/@class="cel-texte cel-neg"') # It is possible to have three or four columns. cols = [self.parser.tocleanstring(td) for td in line.xpath('./td')] date = cols[0] label = cols[1] amount = cols[-1] t = Transaction() t.set_amount(amount) t.label = t.raw = label if is_balance: m = re.search('(\d+ [^ ]+ \d+)', label) if not m: raise BrokenPageError( 'Unable to read card balance in history: %r' % label) if state is None: debit_date = parse_french_date(m.group(1)) else: debit_date = state # Skip the first line because it is balance if i == 0: continue t.date = t.rdate = debit_date # Consider the second one as a positive amount to reset balance to 0. t.amount = -t.amount state = t.date else: day, month = map(int, date.split('/', 1)) t.rdate = date_guesser.guess_date(day, month) t.date = debit_date t.type = t.TYPE_CARD try: t.id = t.unique_id(seen) except UnicodeEncodeError: self.logger.debug(t) self.logger.debug(t.label) raise yield state, t
def get_history(self, date_guesser, state=None): seen = set() lines = self.document.xpath('(//table[@class="ca-table"])[2]/tr') debit_date = None for i, line in enumerate(lines): is_balance = line.xpath('./td/@class="cel-texte cel-neg"') # It is possible to have three or four columns. cols = [self.parser.tocleanstring(td) for td in line.xpath('./td')] date = cols[0] label = cols[1] amount = cols[-1] t = Transaction() t.set_amount(amount) t.label = t.raw = label if is_balance: m = re.search('(\d+ [^ ]+ \d+)', label) if not m: raise BrokenPageError('Unable to read card balance in history: %r' % label) if state is None: debit_date = parse_french_date(m.group(1)) else: debit_date = state # Skip the first line because it is balance if i == 0: continue t.date = t.rdate = debit_date # Consider the second one as a positive amount to reset balance to 0. t.amount = -t.amount state = t.date else: day, month = map(int, date.split('/', 1)) t.rdate = date_guesser.guess_date(day, month) t.date = debit_date t.type = t.TYPE_CARD try: t.id = t.unique_id(seen) except UnicodeEncodeError: self.logger.debug(t) self.logger.debug(t.label) raise yield state, t
def get_operations(self): for tr in self.document.xpath('//div[@class="block no-hd"]//table[@class="list"]/tbody/tr'): tds = tr.xpath('./td') date = self.parser.tocleanstring(tds[0]) label = self.parser.tocleanstring(tds[1]) amount = self.parser.tocleanstring(tds[2]) operation = FrenchTransaction() operation.parse(date=date, raw=label) operation.set_amount(amount) if tds[0].xpath('./a'): operation.investments = self.get_investments(tds[0].xpath('./a')[0].attrib['href']) or NotAvailable yield operation
def get_transactions(self, _type='consommable'): for tr in self.document.xpath('//table[@id="solde_%s_lignes"]/tbody/tr' % _type): cols = tr.findall('td') t = Transaction(0) day, month, year = self.parser.tocleanstring(cols[self.COL_DATE]).split(' ') day = int(day) year = int(year) month = self.MONTHS.index(month.rstrip('.')) + 1 date = datetime.date(year, month, day) label = self.parser.tocleanstring(cols[self.COL_LABEL]) t.parse(date, label) t.set_amount(self.parser.tocleanstring(cols[self.COL_AMOUNT])) yield t
def parse(self): for i, tr in enumerate(self.document.xpath('//tr')): t = FrenchTransaction( tr.xpath('./td[@class="transactionId"]/span')[0].text.strip()) date = parse_french_date( tr.xpath('./td[@class="date"]')[0].text.strip()) status = tr.xpath( './td[@class="desc"]/ul/li[@class="first"]')[0].text.strip() #We pass this because it's not transaction if status == u'Créé' or status == u'Annulé': continue raw = tr.xpath('./td[@class="desc"]/strong')[0].text.strip() t.parse(date=date, raw=raw) amount = tr.xpath('./td[@class="price"]/span')[0].text.strip() t.set_amount(amount) t._currency = Account.get_currency(amount) yield t
def parse_transaction(self, transaction, account): if transaction['transactionStatus'] in [u'Créé', u'Annulé', u'Suspendu', u'Mis à jour', u'Actif']: return t = FrenchTransaction(transaction['transactionId']) if not transaction['transactionAmount']['currencyCode'] == account.currency: cc = self.browser.convert_amount(account, transaction, 'https://www.paypal.com/cgi-bin/webscr?cmd=_history-details-from-hub&id=' + transaction['transactionId']) if not cc: return t.original_amount = Decimal(transaction['transactionAmount']['currencyDoubleValue']) t.original_currency = u'' + transaction['transactionAmount']['currencyCode'] t.set_amount(cc) else: t.amount = Decimal(transaction['transactionAmount']['currencyDoubleValue']) date = parse_french_date(transaction['transactionTime']) raw = transaction['transactionDescription'] t.commission = Decimal(transaction['fee']['currencyDoubleValue']) t.parse(date=date, raw=raw) return t
def get_history(self, guesser): debit_date = self.get_debit_date() if debit_date is not None: guesser.current_date = debit_date for tr in reversed( self.document.xpath( '//div[@id="txnsSection"]//tr[@class="tableStandardText"]') ): cols = tr.findall('td') t = Transaction(tr.attrib['id']) day, month = self.parser.tocleanstring(cols[self.COL_DATE]).split( ' ', 1) day = int(day) month = self.MONTHS.index(month.rstrip('.')) + 1 date = guesser.guess_date(day, month) try: detail = self.parser.select(cols[self.COL_TEXT], 'div.hiddenROC', 1) except BrokenPageError: pass else: detail.drop_tree() raw = (' '.join([ txt.strip() for txt in cols[self.COL_TEXT].itertext() ])).strip() credit = self.parser.tocleanstring(cols[self.COL_CREDIT]) debit = self.parser.tocleanstring(cols[self.COL_DEBIT]) t.date = date t.rdate = date t.raw = re.sub(r'[ ]+', ' ', raw) t.label = re.sub('(.*?)( \d+)? .*', r'\1', raw).strip() t.set_amount(credit, debit) if t.amount > 0: t.type = t.TYPE_ORDER else: t.type = t.TYPE_CARD yield t
def get_history(self): #checking if the card is still valid if self.document.xpath('//div[@id="errorbox"]'): return #adding a time delta because amex have hard time to put the date in a good interval beginning_date = self.get_beginning_debit_date() - datetime.timedelta(days=120) end_date = self.get_end_debit_date() guesser = ChaoticDateGuesser(beginning_date, end_date) for tr in reversed(self.document.xpath('//div[@id="txnsSection"]//tr[@class="tableStandardText"]')): cols = tr.findall('td') t = Transaction(tr.attrib['id']) day, month = self.parser.tocleanstring(cols[self.COL_DATE]).split(' ', 1) day = int(day) month = self.MONTHS.index(month.rstrip('.')) + 1 date = guesser.guess_date(day, month) try: detail = self.parser.select(cols[self.COL_TEXT], 'div.hiddenROC', 1) except BrokenPageError: pass else: detail.drop_tree() raw = (' '.join([txt.strip() for txt in cols[self.COL_TEXT].itertext()])).strip() credit = self.parser.tocleanstring(cols[self.COL_CREDIT]) debit = self.parser.tocleanstring(cols[self.COL_DEBIT]) t.date = date t.rdate = date t.raw = re.sub(r'[ ]+', ' ', raw) t.label = re.sub('(.*?)( \d+)? .*', r'\1', raw).strip() t.set_amount(credit, debit) if t.amount > 0: t.type = t.TYPE_ORDER else: t.type = t.TYPE_CARD yield t
def parse_transaction(self, transaction): t = FrenchTransaction(transaction['activityId']) date = parse_french_date(transaction['date']) try: raw = transaction['counterparty'] except KeyError: raw = transaction['displayType'] t.parse(date=date, raw=raw) try: amount = transaction['netAmount'] except KeyError: return if transaction['isCredit']: t.set_amount(credit=amount) else: t.set_amount(debit=amount) t._currency = transaction['currencyCode'] return t
def parse_transaction(self, transaction): t = FrenchTransaction(transaction['activityId']) date = parse_french_date(transaction['date']) try: raw = transaction['counterparty'] except KeyError: raw = transaction['displayType'] t.parse(date=date, raw=raw) try: amount = transaction['netAmount'] except KeyError: return if transaction['isCredit']: t.set_amount(credit=amount) else: t.set_amount(debit=amount) t._currency = transaction['currencyCode'] return t
def parse(self): for tr in self.document.xpath('//tbody/tr'): tlink = tr.xpath('./td[@class="desc"]/a[@class="rowClick"]')[0].attrib['href'].strip() t = FrenchTransaction(tlink[tlink.find('&id=')+4:]) date = parse_french_date(tr.xpath('./td[@class="date"]')[0].text.strip()) raw = tr.xpath('./td[@class="desc"]/a[@class="rowClick"]')[0].tail.strip() # Filter lines that do not actually modify the balance if raw.startswith('Autorisation ') or raw.endswith(' en attente par PayPal'): continue t.parse(date=date, raw=raw) amount = tr.xpath('./td[@class="price-value net"]')[0].text.strip() t.set_amount(amount) commission = tr.xpath('./td[@class="price-value fee"]')[0].text.strip() t.commission = Decimal(t.clean_amount(commission)) t.label = t.raw if t.commission: t.label += " (%s)" % tr.xpath('./td[@class="price-value gross"]')[0].text.strip() t._currency = Account.get_currency(amount) yield t
def get_history(self): for tr in self.document.xpath('//div[contains(@class, "mod-listeoperations")]//table/tbody/tr'): cols = tr.findall('td') date = self.parser.tocleanstring(cols[0]) raw = self.parser.tocleanstring(cols[1]) label = re.sub(u' - traité le \d+/\d+', '', raw) debit = self.parser.tocleanstring(cols[3]) if len(debit) > 0: t = FrenchTransaction(0) t.parse(date, raw) t.label = label t.set_amount(debit) yield t amount = self.parser.tocleanstring(cols[2]) if len(amount) > 0: t = FrenchTransaction(0) t.parse(date, raw) t.label = label t.set_amount('-' + amount) yield t
def get_history(self, guesser): debit_date = self.get_debit_date() if debit_date is not None: guesser.current_date = debit_date for tr in reversed(self.document.xpath('//div[@id="txnsSection"]//tr[@class="tableStandardText"]')): cols = tr.findall("td") t = Transaction(tr.attrib["id"]) day, month = self.parser.tocleanstring(cols[self.COL_DATE]).split(" ", 1) day = int(day) month = self.MONTHS.index(month.rstrip(".")) + 1 date = guesser.guess_date(day, month) try: detail = self.parser.select(cols[self.COL_TEXT], "div.hiddenROC", 1) except BrokenPageError: pass else: detail.drop_tree() raw = (" ".join([txt.strip() for txt in cols[self.COL_TEXT].itertext()])).strip() credit = self.parser.tocleanstring(cols[self.COL_CREDIT]) debit = self.parser.tocleanstring(cols[self.COL_DEBIT]) t.date = date t.rdate = date t.raw = re.sub(r"[ ]+", " ", raw) t.label = re.sub("(.*?)( \d+)? .*", r"\1", raw).strip() t.set_amount(credit, debit) if t.amount > 0: t.type = t.TYPE_ORDER else: t.type = t.TYPE_CARD yield t
def get_history(self, account): transactions = [] last_order = None for tr in self.document.split('\n')[1:]: cols = tr.split(';') if len(cols) < 4: continue t = FrenchTransaction(0) date = cols[self.COL_DATE] raw = cols[self.COL_TEXT] amount = cols[self.COL_AMOUNT] t.parse(date, re.sub(r'[ ]+', ' ', raw)) if t.raw.startswith('PRELEVEMENT ACHATS DIFFERES'): t._coming = False if last_order is None: last_order = t.date else: t._coming = True t.set_amount(amount) transactions.append(t) # go to the previous stop date before the last order while last_order is not None and last_order.day != account._outstanding_date.day: last_order = last_order - datetime.timedelta(days=1) for t in transactions: if t.date <= last_order: t._coming = False return iter(transactions)
def get_history(self, account): transactions = [] last_order = None for tr in self.document.split('\n')[1:]: cols = tr.split(';') if len(cols) < 4: continue t = FrenchTransaction(0) date = cols[self.COL_DATE] raw = cols[self.COL_TEXT] amount = cols[self.COL_AMOUNT] t.parse(date, re.sub(r'[ ]+', ' ', raw)) if t.raw.startswith('PRELEVEMENT ACHATS DIFFERES'): t._coming = False if last_order is None: last_order = t.date else: t._coming = True t.set_amount(amount) transactions.append(t) # go to the previous stop date before the last order while last_order is not None and last_order.day != account._outstanding_date.day: last_order = last_order - datetime.timedelta(days=1) for t in transactions: if t.date <= last_order: t._coming = False return iter(transactions)
def get_history(self, date_guesser): i = 0 for tr in self.document.xpath('//table[@class="ca-table"]//tr'): parent = tr.getparent() while parent is not None and parent.tag != 'table': parent = parent.getparent() if parent.attrib.get('class', '') != 'ca-table': continue if tr.attrib.get('class', '') == 'tr-thead': heads = tr.findall('th') for i, head in enumerate(heads): key = self.parser.tocleanstring(head) if key == u'Crédit': self.COL_CREDIT = i - len(heads) elif key == u'Débit': self.COL_DEBIT = i - len(heads) elif key == u'Libellé': self.COL_TEXT = i if not tr.attrib.get('class', '').startswith('ligne-'): continue cols = tr.findall('td') # On loan accounts, there is a ca-table with a summary. Skip it. if tr.find('th') is not None or len(cols) < 3: continue t = Transaction(i) col_text = cols[self.COL_TEXT] if len(col_text.xpath('.//br')) == 0: col_text = cols[self.COL_TEXT + 1] raw = self.parser.tocleanstring(col_text) date = self.parser.tocleanstring(cols[self.COL_DATE]) credit = self.parser.tocleanstring(cols[self.COL_CREDIT]) if self.COL_DEBIT is not None: debit = self.parser.tocleanstring(cols[self.COL_DEBIT]) else: debit = '' day, month = map(int, date.split('/', 1)) t.date = date_guesser.guess_date(day, month) t.rdate = t.date t.raw = raw # On some accounts' history page, there is a <font> tag in columns. if col_text.find('font') is not None: col_text = col_text.find('font') t.category = unicode(col_text.text.strip()) t.label = re.sub('(.*) (.*)', r'\2', t.category).strip() sub_label = col_text.find('br').tail if sub_label is not None and ( len(t.label) < 3 or t.label == t.category or len(re.findall('[^\w\s]', sub_label)) / float(len(sub_label)) < len(re.findall('\d', t.label)) / float(len(t.label))): t.label = unicode(sub_label.strip()) # Sometimes, the category contains the label, even if there is another line with it again. t.category = re.sub('(.*) .*', r'\1', t.category).strip() t.type = self.TYPES.get(t.category, t.TYPE_UNKNOWN) # Parse operation date in label (for card transactions for example) m = re.match('(?P<text>.*) (?P<dd>[0-3]\d)/(?P<mm>[0-1]\d)$', t.label) if not m: m = re.match('^(?P<dd>[0-3]\d)/(?P<mm>[0-1]\d) (?P<text>.*)$', t.label) if m: if t.type in (t.TYPE_CARD, t.TYPE_WITHDRAWAL): t.rdate = date_guesser.guess_date( int(m.groupdict()['dd']), int(m.groupdict()['mm']), change_current_date=False) t.label = m.groupdict()['text'].strip() # Strip city or other useless information from label. t.label = re.sub('(.*) .*', r'\1', t.label).strip() t.set_amount(credit, debit) yield t i += 1
def get_history(self, date_guesser): for tr in self.document.xpath('//table[@class="ca-table"]//tr'): parent = tr.getparent() while parent is not None and parent.tag != 'table': parent = parent.getparent() if parent.attrib.get('class', '') != 'ca-table': continue if tr.attrib.get('class', '') == 'tr-thead': heads = tr.findall('th') for i, head in enumerate(heads): key = self.parser.tocleanstring(head) if key == u'Débit': self.COL_DEBIT = i - len(heads) if key == u'Crédit': self.COL_CREDIT = i - len(heads) if key == u'Libellé': self.COL_TEXT = i if not tr.attrib.get('class', '').startswith('ligne-'): continue cols = tr.findall('td') # On loan accounts, there is a ca-table with a summary. Skip it. if tr.find('th') is not None or len(cols) < 3: continue t = Transaction() col_text = cols[self.COL_TEXT] if len(col_text.xpath('.//br')) == 0: col_text = cols[self.COL_TEXT+1] raw = self.parser.tocleanstring(col_text) date = self.parser.tocleanstring(cols[self.COL_DATE]) credit = self.parser.tocleanstring(cols[self.COL_CREDIT]) if self.COL_DEBIT is not None: debit = self.parser.tocleanstring(cols[self.COL_DEBIT]) else: debit = '' day, month = map(int, date.split('/', 1)) t.date = date_guesser.guess_date(day, month) t.rdate = t.date t.raw = raw # On some accounts' history page, there is a <font> tag in columns. if col_text.find('font') is not None: col_text = col_text.find('font') t.category = unicode(col_text.text.strip()) t.label = re.sub('(.*) (.*)', r'\2', t.category).strip() sub_label = col_text.find('br').tail if sub_label is not None and (len(t.label) < 3 or t.label == t.category or len(re.findall('[^\w\s]', sub_label))/float(len(sub_label)) < len(re.findall('\d', t.label))/float(len(t.label))): t.label = unicode(sub_label.strip()) # Sometimes, the category contains the label, even if there is another line with it again. t.category = re.sub('(.*) .*', r'\1', t.category).strip() t.type = self.TYPES.get(t.category, t.TYPE_UNKNOWN) # Parse operation date in label (for card transactions for example) m = re.match('(?P<text>.*) (?P<dd>[0-3]\d)/(?P<mm>[0-1]\d)$', t.label) if not m: m = re.match('^(?P<dd>[0-3]\d)/(?P<mm>[0-1]\d) (?P<text>.*)$', t.label) if m: if t.type in (t.TYPE_CARD, t.TYPE_WITHDRAWAL): t.rdate = date_guesser.guess_date(int(m.groupdict()['dd']), int(m.groupdict()['mm']), change_current_date=False) t.label = m.groupdict()['text'].strip() # Strip city or other useless information from label. t.label = re.sub('(.*) .*', r'\1', t.label).strip() t.set_amount(credit, debit) yield t