def _get_transactions_from_credit_card_statement(self): transactions = [] rows = self._browser.find_elements_by_css_selector('table tr') # Skip header row. rows = rows[1:] for row in rows: try: cells = row.find_elements_by_tag_name('td') # Date. First row is value date, second is voucher date. date_text = cells[1].text.split('\n')[0] date = self._parse_date(date_text) # Memo. memo = fetch.normalize_text(cells[2].text) # Amount. amounts = cells[3].text.split('\n') amount = fetch.parse_decimal_number(amounts[0], 'de_DE') # Currency. currencies = cells[4].text.split('\n') if len(currencies) > 1 and len(amounts) > 1: original_amount = fetch.parse_decimal_number( amounts[1], 'de_DE') original_currency = currencies[1] memo += '\nOriginal amount: %s %.2f' % (original_currency, original_amount) transactions.append(model.Payment(date, amount, memo=memo)) except ValueError, e: logger.warning('Skipping invalid row: %s. Error: %s' % (row.text, e)) raise
def _get_transactions_from_checking_account_statement(self): transactions = [] rows = self._browser.find_elements_by_css_selector( 'table tbody tr.mainRow') for row in rows: try: cells = row.find_elements_by_tag_name('td') # Date. First row is entry date, second is value date. date_text = cells[0].text.split('\n')[1] date = self._parse_date(date_text) # Payee and memo. details_lines = fetch.normalize_text(cells[1].text).split('\n') unused_transaction_type = details_lines[0] payee = '\n'.join( details_lines[1:2]) # This line might not exist. memo = '\n'.join(details_lines[2:]) # Might be empty. payee_lines = cells[2].text.split('\n') + [''] payee_account, payee_clearing = payee_lines[:2] if payee_account: memo += '\nAccount: %s' % payee_account if payee_clearing: memo += '\nClearing: %s' % payee_clearing # Amount amount = fetch.parse_decimal_number(cells[3].text, 'de_DE') transactions.append(model.Payment(date, amount, payee, memo)) except ValueError, e: logger.warning('Skipping invalid row: %s. Error: %s' % (row.text, e)) raise
def pay(): if not flask_security.current_user.is_authenticated: return redirect('/login') if not flask_security.current_user.id == int(app.config['ADMIN_ID']): flask.abort(404) users = model.get_users() users_choices = [] for u in users: users_choices.append((str(u.id), ("%s %s" % (str(u.first_name), str(u.last_name))))) print(users_choices) form = forms.PaymentForm() form.user.choices = users_choices if form.validate_on_submit(): user_id = int(form.user.data) users = (model.User.query .filter(model.User.approved == True) .filter(model.User.participates == True) .filter(model.User.id != user_id)).all() amount = form.amount.data birthday = model.Birthday() birthday.amount = int(amount) birthday.user_id = user_id birthday.gift = form.gift.data db.session.add(birthday) num_users = int(len(users)) individual_payment = int(birthday.amount / num_users) for user in users: payment = model.Payment() payment.amount = individual_payment payment.birthday = birthday payment.user = user payment.timestamp = datetime.datetime.utcnow() db.session.add(payment) db.session.commit() for user in users: tasks.pay.delay(user.id, int(individual_payment), user_id, birthday.gift) return flask.redirect(flask.url_for( 'pay')) return render_template('pay.html', form=form)
def _get_transfers(self, csv_dict, account_name): logger.debug('Extracting transfers...') transactions_by_currency = collections.defaultdict(list) for row in csv_dict['Deposits & Withdrawals']['Data']['__rows']: currency = row[0] if currency.startswith('Total'): continue date = datetime.datetime.strptime(row[1], self._DATE_FORMAT) kind = row[2] amount = self._parse_float(row[3]) transaction = model.Payment(date, amount, payee=account_name) transactions_by_currency[currency].append(transaction) return transactions_by_currency
def _parse_transaction_from_text(self, date, memo, amount): try: date = datetime.datetime.strptime(date, self._DATE_FORMAT) except ValueError: logger.warning('Skipping transaction with invalid date %s.', date) return memo = fetch.normalize_text(memo) try: amount = fetch.parse_decimal_number(amount, 'de_CH') except ValueError: logger.warning('Skipping transaction with invalid amount %s.', amount) return return model.Payment(date, amount, memo=memo)
def _get_credit_card_transactions(self, account, start, end): browser = self._browser logger.info('Opening credit cards overview...') self._go_to_assets() cc_tile = self._get_tile_by_title('Credit card') fetch.find_element_by_title(cc_tile, 'Detailed overview').click() self._wait_to_finish_loading() content = browser.find_element_by_class_name('detail_page') logger.debug('Finding credit card account...') # Switch to tab for that account. for tab in content.find_elements_by_css_selector('tab-wrapper'): if tab.text.endswith(account.name[-4:]): tab.find_element_by_tag_name('a').click() # Verify that the correct card is displayed. active_pane = content.find_element_by_css_selector( 'section.js-tabs--pane.is-active') formatted_account_name = fetch.format_cc_account_name(account.name) if formatted_account_name not in active_pane.text: raise fetch.FetchError('Couldn\'t find account %s.' % account) # You can see the transactions for one month/period at a time. transactions = [] while True: self._wait_to_finish_loading() # Get the period of the current page. date_select_el = content.find_element_by_css_selector( '.buttons select') date_select = ui.Select(date_select_el) period = date_select.first_selected_option.text if period == 'Current accounting period': # Just use "now", which is an inaccurate hack, but works for our # purposes. start_date = end_date = datetime.datetime.now() else: match = self._CREDIT_CARD_DATE_RANGE_PATTERN.search(period) if match: start_date_str = match.group(1) end_date_str = match.group(2) start_date = datetime.datetime.strptime( start_date_str, self._DATE_FORMAT) end_date = datetime.datetime.strptime( end_date_str, self._DATE_FORMAT) else: raise fetch.FetchError( 'Not a credit card transactions page %s.' % account.name) logger.debug('Current period: ' + period) transactions_on_page = self._extract_cc_transactions() transactions += transactions_on_page logger.debug('Found %i transactions on the current page.' % len(transactions_on_page)) # Are we done yet? if start_date <= start: logger.info('Should have loaded enough transaction pages.') break else: logger.debug('Adding marker transaction for page break.') if transactions: transactions.append( model.Payment(transactions[-1].date, amount=0, memo='[Next billing cycle]')) # Load earlier transactions. next_option = date_select_el.find_element_by_xpath( "option[text() = '%s']/following-sibling::option" % period) if not next_option: logger.info('No more earlier transactions.') break logger.info('Loading earlier transactions page...') date_select.select_by_value(next_option.get_attribute('value')) self._wait_to_finish_loading() # Filter the transactions for the requested date range. logger.debug('Found %i transactions before filtering for date range.' % len(transactions)) transactions = filter(lambda t: start <= t.date < end, transactions) # They should be sorted in reverse chronological order already, but # let's make this explicit. transactions.sort(key=lambda t: t.date, reverse=True) logger.info('Found %i transactions.' % len(transactions)) self._close_tile() return transactions