def get_history(self, currency): self.MONTHS = self.FR_MONTHS if currency == 'EUR' else self.US_MONTHS #checking if the card is still valid if self.doc.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=360) end_date = self.get_end_debit_date() guesser = ChaoticDateGuesser(beginning_date, end_date) for tr in reversed( self.doc.xpath( '//div[@id="txnsSection"]//tr[@class="tableStandardText"]') ): cols = tr.findall('td') t = Transaction() day, month = CleanText().filter(cols[self.COL_DATE]).split(' ', 1) day = int(day) month = self.MONTHS.index(month.rstrip('.')) + 1 date = guesser.guess_date(day, month) rdate = None try: detail = cols[self.COL_TEXT].xpath( './div[has-class("hiddenROC")]')[0] except IndexError: pass else: m = re.search(r' (\d{2} \D{3,4})', (' '.join( [txt.strip() for txt in detail.itertext()])).strip()) if m: rday, rmonth = m.group(1).strip().split(' ') rday = int(rday) rmonth = self.MONTHS.index(rmonth.rstrip('.')) + 1 rdate = guesser.guess_date(rday, rmonth) detail.drop_tree() raw = (' '.join([ txt.strip() for txt in cols[self.COL_TEXT].itertext() ])).strip() credit = CleanText().filter(cols[self.COL_CREDIT]) debit = CleanText().filter(cols[self.COL_DEBIT]) t.date = date t.rdate = rdate or date t.raw = re.sub(r'[ ]+', ' ', raw) t.label = re.sub('(.*?)( \d+)? .*', r'\1', raw).strip() t.amount = CleanDecimal(replace_dots=currency == 'EUR').filter( credit or debit) * (1 if credit else -1) if t.amount > 0: t.type = t.TYPE_ORDER else: t.type = t.TYPE_CARD yield t
def get_history(self, currency): self.MONTHS = self.FR_MONTHS if currency == 'EUR' else self.US_MONTHS #checking if the card is still valid if self.doc.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=300) end_date = self.get_end_debit_date() guesser = ChaoticDateGuesser(beginning_date, end_date) for tr in reversed(self.doc.xpath('//div[@id="txnsSection"]//tr[@class="tableStandardText"]')): cols = tr.findall('td') t = Transaction() day, month = CleanText().filter(cols[self.COL_DATE]).split(' ', 1) day = int(day) month = self.MONTHS.index(month.rstrip('.')) + 1 date = guesser.guess_date(day, month) rdate = None try: detail = cols[self.COL_TEXT].xpath('./div[has-class("hiddenROC")]')[0] except IndexError: pass else: m = re.search(r' (\d{2} \D{3,4})', (' '.join([txt.strip() for txt in detail.itertext()])).strip()) if m: rday, rmonth = m.group(1).split(' ') rday = int(rday) rmonth = self.MONTHS.index(rmonth.rstrip('.')) + 1 rdate = guesser.guess_date(rday, rmonth) detail.drop_tree() raw = (' '.join([txt.strip() for txt in cols[self.COL_TEXT].itertext()])).strip() credit = CleanText().filter(cols[self.COL_CREDIT]) debit = CleanText().filter(cols[self.COL_DEBIT]) t.date = date t.rdate = rdate or date t.raw = re.sub(r'[ ]+', ' ', raw) t.label = re.sub('(.*?)( \d+)? .*', r'\1', raw).strip() t.amount = CleanDecimal(replace_dots=currency == 'EUR').filter(credit or debit) * (1 if credit else -1) 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 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): #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 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, 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, 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
def get_history(self, account): # checking if the card is still valid if self.doc.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=360) end_date = self.get_end_debit_date() guesser = ChaoticDateGuesser(beginning_date, end_date) # Since the site doesn't provide the debit_date, # we just use the date of beginning of the previous period. # If this date + 1 month is greater than today's date, # then the transaction is coming end_of_period = None previous_date = CleanText('//td[@id="colStatementBalance"]/div[3]', default=None)(self.doc) if previous_date: end_of_period = (parse_french_date(' '.join(previous_date.split()[1:4])) + relativedelta(months=1)).date() else: previous_date = CleanText('//select[@id="viewPeriod"]/option[@selected]', default=None)(self.doc) if previous_date: end_of_period = parse_french_date(' '.join(previous_date.split()[:3])) + relativedelta(days=-1) + relativedelta(months=1) end_of_period = end_of_period.date() _id = str(int(account._idforold)) for tr in reversed(self.doc.xpath('//div[@id="txnsSection"]//tbody[@id="tableBody-txnsCard%s"]/tr[@class="tableStandardText"]' % _id)): cols = tr.findall('td') t = Transaction() day, month = CleanText().filter(cols[self.COL_DATE]).split(' ', 1) day = int(day) month = self.parse_month(month) date = guesser.guess_date(day, month) vdate = None try: detail = cols[self.COL_TEXT].xpath('./div[has-class("hiddenROC")]')[0] except IndexError: pass else: m = re.search(r' (\d{2} \D{3,4})', (' '.join([txt.strip() for txt in detail.itertext()])).strip()) if m: vday, vmonth = m.group(1).strip().split(' ') vday = int(vday) vmonth = self.parse_month(vmonth) vdate = guesser.guess_date(vday, vmonth) detail.drop_tree() raw = (' '.join([txt.strip() for txt in cols[self.COL_TEXT].itertext()])).strip() credit = CleanText().filter(cols[self.COL_CREDIT]) debit = CleanText().filter(cols[self.COL_DEBIT]) if end_of_period is not None and datetime.date.today() < end_of_period: t._is_coming = True else: t._is_coming = False t.date = t.rdate = date t.vdate = vdate t.raw = re.sub(r'[ ]+', ' ', raw) t.label = re.sub('(.*?)( \d+)? .*', r'\1', raw).strip() t.amount = parse_decimal(credit or debit) * (1 if credit else -1) if t.raw in self.browser.SUMMARY_CARD_LABEL: t.type = t.TYPE_CARD_SUMMARY elif t.amount > 0: t.type = t.TYPE_ORDER else: t.date = end_of_period t.type = t.TYPE_DEFERRED_CARD yield t
def get_history(self, currency): # checking if the card is still valid if self.doc.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=360) end_date = self.get_end_debit_date() guesser = ChaoticDateGuesser(beginning_date, end_date) # Since the site doesn't provide the debit_date, # we just use the date of beginning of the previous period. # If this date + 1 month is greater than today's date, # then the transaction is coming end_of_period = None previous_date = CleanText('//td[@id="colStatementBalance"]/div[3]', default=None)(self.doc) if previous_date: end_of_period = ( parse_french_date(' '.join(previous_date.split()[1:4])) + relativedelta(months=1)).date() else: previous_date = CleanText( '//select[@id="viewPeriod"]/option[@selected]', default=None)(self.doc) if previous_date: end_of_period = parse_french_date(' '.join( previous_date.split()[:3])) + relativedelta( days=-1) + relativedelta(months=1) end_of_period = end_of_period.date() for tr in reversed( self.doc.xpath( '//div[@id="txnsSection"]//tbody/tr[@class="tableStandardText"]' )): cols = tr.findall('td') t = Transaction() day, month = CleanText().filter(cols[self.COL_DATE]).split(' ', 1) day = int(day) month = self.parse_month(month) date = guesser.guess_date(day, month) vdate = None try: detail = cols[self.COL_TEXT].xpath( './div[has-class("hiddenROC")]')[0] except IndexError: pass else: m = re.search(r' (\d{2} \D{3,4})', (' '.join( [txt.strip() for txt in detail.itertext()])).strip()) if m: vday, vmonth = m.group(1).strip().split(' ') vday = int(vday) vmonth = self.parse_month(vmonth) vdate = guesser.guess_date(vday, vmonth) detail.drop_tree() raw = (' '.join([ txt.strip() for txt in cols[self.COL_TEXT].itertext() ])).strip() credit = CleanText().filter(cols[self.COL_CREDIT]) debit = CleanText().filter(cols[self.COL_DEBIT]) if end_of_period is not None and datetime.date.today( ) < end_of_period: t._is_coming = True else: t._is_coming = False t.date = t.rdate = date t.vdate = vdate t.raw = re.sub(r'[ ]+', ' ', raw) t.label = re.sub('(.*?)( \d+)? .*', r'\1', raw).strip() t.amount = parse_decimal(credit or debit) * (1 if credit else -1) if t.amount > 0: t.type = t.TYPE_ORDER else: t.date = end_of_period t.type = t.TYPE_DEFERRED_CARD yield t