A script to convert CSV exported from Sparebanken Sør to a format YNAB can import
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

113 line
2.8KB

  1. #!/bin/python3
  2. import sys
  3. import csv
  4. import re
  5. from pathlib import Path
  6. whitelist_cards = ["7756"]
  7. def usage():
  8. print('Usage: ynab.py <filename>')
  9. def is_reserved(row):
  10. return '(Reservert)' in row[1]
  11. def convert_memo(original):
  12. original = re.match(r'="[ ]?(.+)"', original).groups()[0]
  13. original = original.replace(" Kurs: 1.0000", "")
  14. words = original.split(" ")
  15. for i in range(20):
  16. if words[0] == "":
  17. # It's empty
  18. del words[0]
  19. elif m := re.match(r'\*(\d{4})', words[0]):
  20. # It's the last four digits of a card
  21. if m.groups()[0] in whitelist_cards:
  22. # It's an expected card, ignore it
  23. del words[0]
  24. else:
  25. # It's an unexpected card, move it to the end
  26. words.append(words.pop(0))
  27. elif m := re.match(r'\d{2}\.\d{2}', words[0]):
  28. # It's the date. Move it to the end
  29. words.append(words.pop(0))
  30. elif (m1 := re.match(r'^[A-Z]{3}$', words[0])) and (m2 := re.match(r'[\d]+\.[\d]+', words[1])):
  31. # It's the original currency
  32. if words[0] == "NOK":
  33. # It's Norwegian kroner, ignoring
  34. del words[0]
  35. del words[0]
  36. else:
  37. # It's some other currency, move it to the end
  38. words.append(words.pop(0))
  39. words.append(words.pop(0))
  40. else:
  41. break
  42. else:
  43. raise Exception(f"Infinite loop while parsing \"{original}\"")
  44. return " ".join(words)
  45. def convert(reader, writer):
  46. header = ['Date', 'Payee', 'Memo', 'Outflow', 'Inflow']
  47. writer.writerow(header)
  48. # Ignore header
  49. reader.__next__()
  50. for row in reader:
  51. if is_reserved(row):
  52. continue
  53. money_out = 0
  54. money_in = 0
  55. if (isinstance(row[3], str)):
  56. moneystr = row[3].replace(',', '.')
  57. try:
  58. money = float(moneystr) if moneystr else 0
  59. except ValueError as e:
  60. print(f'Error in row {row}')
  61. raise e
  62. else:
  63. money = float(row[3])
  64. if money >= 0:
  65. money_in = money
  66. else:
  67. money_out = -money
  68. memo = convert_memo(row[2])
  69. new_row = [*row[:2], memo, money_out, money_in]
  70. writer.writerow(new_row)
  71. def main():
  72. if len(sys.argv) != 2:
  73. usage()
  74. exit(1)
  75. filepath = Path(sys.argv[1])
  76. new_basename = f'ynab-{filepath.name}'
  77. new_filepath = filepath.parent / new_basename
  78. with filepath.open(mode='r') as old_file:
  79. reader = csv.reader(old_file, delimiter=';')
  80. with new_filepath.open(mode='w') as new_file:
  81. writer = csv.writer(new_file)
  82. convert(reader, writer)
  83. if __name__ == '__main__':
  84. main()