| @@ -0,0 +1,138 @@ | |||
| # Created by https://www.toptal.com/developers/gitignore/api/python | |||
| # Edit at https://www.toptal.com/developers/gitignore?templates=python | |||
| ### Python ### | |||
| # Byte-compiled / optimized / DLL files | |||
| __pycache__/ | |||
| *.py[cod] | |||
| *$py.class | |||
| # C extensions | |||
| *.so | |||
| # Distribution / packaging | |||
| .Python | |||
| build/ | |||
| develop-eggs/ | |||
| dist/ | |||
| downloads/ | |||
| eggs/ | |||
| .eggs/ | |||
| lib/ | |||
| lib64/ | |||
| parts/ | |||
| sdist/ | |||
| var/ | |||
| wheels/ | |||
| pip-wheel-metadata/ | |||
| share/python-wheels/ | |||
| *.egg-info/ | |||
| .installed.cfg | |||
| *.egg | |||
| MANIFEST | |||
| # PyInstaller | |||
| # Usually these files are written by a python script from a template | |||
| # before PyInstaller builds the exe, so as to inject date/other infos into it. | |||
| *.manifest | |||
| *.spec | |||
| # Installer logs | |||
| pip-log.txt | |||
| pip-delete-this-directory.txt | |||
| # Unit test / coverage reports | |||
| htmlcov/ | |||
| .tox/ | |||
| .nox/ | |||
| .coverage | |||
| .coverage.* | |||
| .cache | |||
| nosetests.xml | |||
| coverage.xml | |||
| *.cover | |||
| *.py,cover | |||
| .hypothesis/ | |||
| .pytest_cache/ | |||
| # Translations | |||
| *.mo | |||
| *.pot | |||
| # Django stuff: | |||
| *.log | |||
| local_settings.py | |||
| db.sqlite3 | |||
| db.sqlite3-journal | |||
| # Flask stuff: | |||
| instance/ | |||
| .webassets-cache | |||
| # Scrapy stuff: | |||
| .scrapy | |||
| # Sphinx documentation | |||
| docs/_build/ | |||
| # PyBuilder | |||
| target/ | |||
| # Jupyter Notebook | |||
| .ipynb_checkpoints | |||
| # IPython | |||
| profile_default/ | |||
| ipython_config.py | |||
| # pyenv | |||
| .python-version | |||
| # pipenv | |||
| # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. | |||
| # However, in case of collaboration, if having platform-specific dependencies or dependencies | |||
| # having no cross-platform support, pipenv may install dependencies that don't work, or not | |||
| # install all needed dependencies. | |||
| #Pipfile.lock | |||
| # PEP 582; used by e.g. github.com/David-OConnor/pyflow | |||
| __pypackages__/ | |||
| # Celery stuff | |||
| celerybeat-schedule | |||
| celerybeat.pid | |||
| # SageMath parsed files | |||
| *.sage.py | |||
| # Environments | |||
| .env | |||
| .venv | |||
| env/ | |||
| venv/ | |||
| ENV/ | |||
| env.bak/ | |||
| venv.bak/ | |||
| # Spyder project settings | |||
| .spyderproject | |||
| .spyproject | |||
| # Rope project settings | |||
| .ropeproject | |||
| # mkdocs documentation | |||
| /site | |||
| # mypy | |||
| .mypy_cache/ | |||
| .dmypy.json | |||
| dmypy.json | |||
| # Pyre type checker | |||
| .pyre/ | |||
| # pytype static type analyzer | |||
| .pytype/ | |||
| # End of https://www.toptal.com/developers/gitignore/api/python | |||
| @@ -0,0 +1,16 @@ | |||
| # A small script that converts CSV output from Sparebanken Sør to a format that YNAB can import | |||
| Usage: | |||
| ```bash | |||
| python ynab.py <path-to-csv-file> | |||
| ``` | |||
| When eksporting from Sparebanken Sør, the file will usually be called `transaksjoner.csv` and will be placed in the Downloads folder, | |||
| so the command will usually be: | |||
| ```bash | |||
| python ynab.py Downloads/transaksjoner.csv | |||
| ``` | |||
| A new CSV file will be created in the same folder, with `ynab-` prepended to the filename. | |||
| @@ -0,0 +1,64 @@ | |||
| #!/bin/python3 | |||
| import sys | |||
| import csv | |||
| import re | |||
| from pathlib import Path | |||
| def usage(): | |||
| print('Usage: ynab.py <filename>') | |||
| def is_final(row): | |||
| return row[2] not in ['VISA', 'Varekjøp'] | |||
| def convert(reader, writer): | |||
| header = ['Date', 'Payee', 'Memo', 'Outflow', 'Inflow'] | |||
| writer.writerow(header) | |||
| # Ignore header | |||
| reader.__next__() | |||
| for row in reader: | |||
| if not is_final(row): | |||
| continue | |||
| money_out = 0 | |||
| money_in = 0 | |||
| money = float(row[3].replace(',', '.')) | |||
| if money >= 0: | |||
| money_in = money | |||
| else: | |||
| money_out = -money | |||
| memo = re.match(r'="[ ]?(.+)"', row[2]).groups()[0] | |||
| new_row = [*row[:2], memo, money_out, money_in] | |||
| writer.writerow(new_row) | |||
| 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') 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() | |||