| @@ -7,15 +7,18 @@ from pathlib import Path | |||||
| whitelist_cards = ["7756"] | whitelist_cards = ["7756"] | ||||
| in_header = ['Utført dato', 'Bokført dato', 'Rentedato', 'Beskrivelse', 'Type', 'Undertype', 'Fra konto', 'Avsender', 'Til konto', 'Mottakernavn', 'Beløp inn', 'Beløp ut', 'Valuta', 'Status', 'Melding'] | |||||
| out_header = ['Date', 'Payee', 'Memo', 'Outflow', 'Inflow'] | |||||
| def usage(): | def usage(): | ||||
| print('Usage: ynab.py <filename>') | print('Usage: ynab.py <filename>') | ||||
| def is_reserved(row): | def is_reserved(row): | ||||
| return '(Reservert)' in row[1] | |||||
| return row['Status'] == "Reservert" | |||||
| def convert_memo(original): | def convert_memo(original): | ||||
| original = re.match(r'="[ ]?(.+)"', original).groups()[0] | |||||
| original = original.replace(" Kurs: 1.0000", "") | original = original.replace(" Kurs: 1.0000", "") | ||||
| words = original.split(" ") | words = original.split(" ") | ||||
| @@ -53,41 +56,45 @@ def convert_memo(original): | |||||
| return " ".join(words) | return " ".join(words) | ||||
| def convert(reader, writer): | |||||
| header = ['Date', 'Payee', 'Memo', 'Outflow', 'Inflow'] | |||||
| writer.writerow(header) | |||||
| def convert_row(row): | |||||
| if is_reserved(row): | |||||
| return None | |||||
| # Ignore header | |||||
| reader.__next__() | |||||
| if row['Valuta'] != 'NOK': | |||||
| raise ValueError(f"Unknown currency {row['Valuta']}") | |||||
| for row in reader: | |||||
| if is_reserved(row): | |||||
| continue | |||||
| return [ | |||||
| row['Bokført dato'], | |||||
| row['Beskrivelse'], | |||||
| convert_memo(row['Melding']), | |||||
| -float(row['Beløp ut'] or '0'), | |||||
| float(row['Beløp inn'] or '0'), | |||||
| ] | |||||
| money_out = 0 | |||||
| money_in = 0 | |||||
| if (isinstance(row[3], str)): | |||||
| moneystr = row[3].replace(',', '.') | |||||
| def convert(reader, writer): | |||||
| writer.writerow(out_header) | |||||
| try: | |||||
| money = float(moneystr) if moneystr else 0 | |||||
| except ValueError as e: | |||||
| print(f'Error in row {row}') | |||||
| raise e | |||||
| else: | |||||
| money = float(row[3]) | |||||
| # Ignore header | |||||
| reader.__next__() | |||||
| if money >= 0: | |||||
| money_in = money | |||||
| for raw_row in reader: | |||||
| # Stop when we hit an empty row | |||||
| for field in raw_row: | |||||
| if field: | |||||
| break | |||||
| else: | else: | ||||
| money_out = -money | |||||
| break | |||||
| memo = convert_memo(row[2]) | |||||
| # Create dictionary with proper field names | |||||
| row = {x[0]: x[1] for x in zip(in_header, raw_row)} | |||||
| new_row = [*row[:2], memo, money_out, money_in] | |||||
| writer.writerow(new_row) | |||||
| try: | |||||
| if (result := convert_row(row)) is not None: | |||||
| writer.writerow(result) | |||||
| except Exception as e: | |||||
| print(f'Error in row {row}') | |||||
| raise e | |||||
| def main(): | def main(): | ||||
| @@ -100,7 +107,7 @@ def main(): | |||||
| new_basename = f'ynab-{filepath.name}' | new_basename = f'ynab-{filepath.name}' | ||||
| new_filepath = filepath.parent / new_basename | new_filepath = filepath.parent / new_basename | ||||
| with filepath.open(mode='r') as old_file: | |||||
| with filepath.open(mode='r', encoding='latin1') as old_file: | |||||
| reader = csv.reader(old_file, delimiter=';') | reader = csv.reader(old_file, delimiter=';') | ||||
| with new_filepath.open(mode='w') as new_file: | with new_filepath.open(mode='w') as new_file: | ||||