Created
January 10, 2021 16:10
-
-
Save bernikr/98a8526bf59494dc6ce35d2cb166f6db to your computer and use it in GitHub Desktop.
Custom YNAV4 to Firefly III import
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import csv | |
import re | |
from operator import itemgetter, methodcaller | |
from api import FireflyAPI | |
token = 'TOKEN' | |
account_map = {} | |
investment_change_opposite = 'OPPOSING ACCOUNT' | |
investment_change_payees = [] | |
def parse_transaction(t): | |
def map_account(acc): | |
return account_map.get(acc, acc) | |
transaction = { | |
'type': None, | |
'date': '{y}-{m}-{d}'.format(**re.match(r'(?P<d>\d+)\.(?P<m>\d+)\.(?P<y>\d+)', t['Date']).groupdict()), | |
'amount': max(map(lambda x: abs(float(x[:-1].replace(',', '.'))), [t['Outflow'], t['Inflow']])), | |
'description': t['Payee'], | |
'notes': t['Memo'], | |
'tags': ['YNAB import'], | |
'reconciled': t['Cleared'] == 'R' | |
} | |
if t['Payee'].startswith('Transfer : '): | |
transaction['type'] = 'transfer' | |
transaction['source_name'] = map_account(t['Account']) | |
transaction['destination_name'] = map_account(t['Payee'][11:]) | |
transaction['description'] = t['Memo'] if t['Memo'] != "" else "Transfer" | |
elif t['Payee'] in investment_change_payees: | |
transaction['type'] = 'transfer' | |
transaction['description'] = 'Kursgewinn/verlust' | |
if t['Outflow'] != '0,00€': | |
transaction['source_name'] = map_account(t['Account']) | |
transaction['destination_name'] = investment_change_opposite | |
elif t['Inflow'] != '0,00€': | |
transaction['destination_name'] = map_account(t['Account']) | |
transaction['source_name'] = investment_change_opposite | |
else: | |
transaction['type'] = None | |
elif t['Outflow'] != '0,00€': | |
transaction['type'] = 'withdrawal' | |
transaction['source_name'] = map_account(t['Account']) | |
transaction['destination_name'] = t['Payee'] | |
transaction['category_name'] = t['Category'] | |
elif t['Inflow'] != '0,00€': | |
transaction['type'] = 'deposit' | |
transaction['destination_name'] = map_account(t['Account']) | |
transaction['source_name'] = t['Payee'] | |
return transaction | |
if __name__ == '__main__': | |
# Load Data | |
with open('input.csv', encoding='utf-8-sig') as csvfile: | |
transactions = list(csv.DictReader(csvfile, delimiter='\t')) | |
accounts = list(set(map(itemgetter('Account'), transactions))) | |
for t in transactions: | |
# Rename Payees that have same name as account so that the automapper doesnt get confused | |
if t['Payee'].lower() in map(methodcaller('lower'), accounts): | |
t['Payee'] += " Bank" | |
# Remove Payee from split Payee/Transfer Transactions | |
if '/ Transfer :' in t['Payee']: | |
t['Payee'] = 'Transfer :' + t['Payee'].split('/ Transfer :')[1] | |
# Remove one side of every Transfer (Only keep those with Outflow) | |
transactions = list(filter(lambda t: not t['Payee'].startswith('Transfer : ') or t['Outflow'] != '0,00€', | |
transactions)) | |
# Parse Transactions into dict for API | |
transactions = list(map(parse_transaction, transactions)) | |
# filter out invalid transactions (e.g. In=Out=0,00€) | |
transactions = list(filter(lambda x: x['type'] is not None, transactions)) | |
# map accounts | |
for t in transactions: | |
t['source_name'] = account_map.get(t['source_name'], t['source_name']) | |
if 'destination_name' in t: | |
t['destination_name'] = account_map.get(t['destination_name'], t['destination_name']) | |
# check if asset accounts exist | |
api = FireflyAPI('FIREFLYURL', token) | |
accounts = api.request('/api/v1/accounts?type=asset') | |
accounts = list(map(itemgetter('name'), map(itemgetter('attributes'), accounts))) | |
for t in transactions: | |
if t['type'] in ['withdrawal', 'transfer'] and t['source_name'] not in accounts: | |
raise Exception('Account {} does not exist'.format(t['source_name'])) | |
if t['type'] in ['deposit', 'transfer'] and t['destination_name'] not in accounts: | |
raise Exception('Account {} does not exist'.format(t['destination_name'])) | |
l = len(transactions) | |
for i, t in enumerate(transactions): | |
data = { | |
'transactions': [t] | |
} | |
print('{}% {}/{}'.format((i + 1) * 100 // l, i + 1, l)) | |
api.request('/api/v1/transactions', method='POST', data=data) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment