|
- #!/bin/python3
-
- import sys
- import csv
- import re
- from pathlib import Path
-
- 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():
- print('Usage: ynab.py <filename>')
-
-
- def is_reserved(row):
- return row['Status'] == "Reservert"
-
- def convert_memo(original):
- original = original.replace(" Kurs: 1.0000", "")
- words = original.split(" ")
-
- for i in range(20):
- if words[0] == "":
- # It's empty
- del words[0]
- elif m := re.match(r'\*(\d{4})', words[0]):
- # It's the last four digits of a card
- if m.groups()[0] in whitelist_cards:
- # It's an expected card, ignore it
- del words[0]
- else:
- # It's an unexpected card, move it to the end
- words.append(words.pop(0))
- elif m := re.match(r'\d{2}\.\d{2}', words[0]):
- # It's the date. Move it to the end
- words.append(words.pop(0))
- elif (m1 := re.match(r'^[A-Z]{3}$', words[0])) and (m2 := re.match(r'[\d]+\.[\d]+', words[1])):
- # It's the original currency
- if words[0] == "NOK":
- # It's Norwegian kroner, ignoring
- del words[0]
- del words[0]
- else:
- # It's some other currency, move it to the end
- words.append(words.pop(0))
- words.append(words.pop(0))
- else:
- break
- else:
- raise Exception(f"Infinite loop while parsing \"{original}\"")
-
-
- return " ".join(words)
-
-
- def convert_row(row):
- if is_reserved(row):
- return None
-
- if row['Valuta'] != 'NOK':
- raise ValueError(f"Unknown currency {row['Valuta']}")
-
- 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'),
- ]
-
-
- def convert(reader, writer):
- writer.writerow(out_header)
-
- # Ignore header
- reader.__next__()
-
- for raw_row in reader:
- # Stop when we hit an empty row
- for field in raw_row:
- if field:
- break
- else:
- break
-
- # Create dictionary with proper field names
- row = {x[0]: x[1] for x in zip(in_header, raw_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():
- if len(sys.argv) != 2:
- usage()
- exit(1)
-
- filepath = Path(sys.argv[1])
-
- new_basename = f'ynab-{filepath.name}'
- new_filepath = filepath.parent / new_basename
-
- with filepath.open(mode='r', encoding='latin1') as old_file:
- reader = csv.reader(old_file, delimiter=';')
-
- with new_filepath.open(mode='w') as new_file:
- writer = csv.writer(new_file)
- convert(reader, writer)
-
-
- if __name__ == '__main__':
- main()
|