diff --git a/Pipfile b/Pipfile index 67249df..dbaf9a8 100644 --- a/Pipfile +++ b/Pipfile @@ -13,6 +13,7 @@ prompt-toolkit = "*" bottle = "*" mt-940 = "*" click = "*" +xdg = "*" [requires] python_version = "3.8" diff --git a/Pipfile.lock b/Pipfile.lock index 579854a..0c7fde8 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "731c3810c49b4b77cd9aac8450a97650031dc994b7367106567d351f96011a24" + "sha256": "45bd6b24d71ce0251c64ad6cad358882053f9cacc52bb418fce22889da6ff702" }, "pipfile-spec": 6, "requires": { @@ -61,6 +61,14 @@ "sha256:f28b3e8a6483e5d49e7f8949ac1a78314e740333ae305b4ba5defd3e74fb37a8" ], "version": "==0.1.8" + }, + "xdg": { + "hashes": [ + "sha256:bf9032b027e3061d38c362a21b14dcf057a5b5a4906956f8e8278cefdf73f38b", + "sha256:c939c99def394cbaf765a3ee55efd6ea7e4c5eaed8d9ebc2d03af84ba35dec57" + ], + "index": "pypi", + "version": "==4.0.1" } }, "develop": { diff --git a/README.md b/README.md index ff1d496..d7e9318 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ ## ToDo -- Profile via config files im home - Graphen - Linechart Verlauf über Monat (Gesamtetrag) - Piechart verschiedene Kategorien diff --git a/cli b/cli index 4cb0f7c..467fd33 100755 --- a/cli +++ b/cli @@ -1,13 +1,41 @@ #! /usr/bin/env python3 import click -import sort, importer, serve +import os +import sys + +import sort +import importer +import serve + +from helper import build_database_filename, create_dirs +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker + +from sqlalchemy.ext.declarative import declarative_base +import models + @click.group() def cli(): pass + +@cli.command(name="init") +@click.argument("profile_name", required=True) +def init(profile_name): + filename = build_database_filename(profile_name) + if os.path.exists(filename): + print(f"Profile '{profile_name}' already exists") + sys.exit(1) + else: + engine = create_engine(f"sqlite:///{filename}") + models.Base.metadata.create_all(engine) + print(f"Sucessfully create the profile '{profile_name}'") + + if __name__ == '__main__': + create_dirs() cli.add_command(sort.command) cli.add_command(importer.command) cli.add_command(serve.command) - cli() \ No newline at end of file + cli() diff --git a/create.py b/create.py deleted file mode 100644 index d835372..0000000 --- a/create.py +++ /dev/null @@ -1,14 +0,0 @@ -#! /usr/bin/env python3 -from sqlalchemy import create_engine -from sqlalchemy.orm import sessionmaker -from sqlalchemy.ext.declarative import declarative_base - -engine = create_engine("sqlite:///app.db") -Base = declarative_base() - -Session = sessionmaker(bind=engine) -session = Session() - -from models import * - -Base.metadata.create_all(engine) diff --git a/helper.py b/helper.py new file mode 100644 index 0000000..8defd4e --- /dev/null +++ b/helper.py @@ -0,0 +1,42 @@ +#! /usr/bin/env python3 +import os +import sys +from xdg import XDG_CONFIG_HOME, XDG_DATA_HOME +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker + +CONFIG_DIR = os.path.join(XDG_CONFIG_HOME, "schmeckels") +DATA_DIR = os.path.join(XDG_DATA_HOME, "schmeckels") + + +def create_dirs(): + for directory in [CONFIG_DIR, DATA_DIR]: + try: + os.mkdir(directory) + except FileExistsError: + pass + + +def build_database_filename(profile_name): + return f"{DATA_DIR}/{profile_name}.db" + + +def get_session(profile_name): + if not profile_name: + count = len(os.listdir(DATA_DIR)) + if count == 1: + filename = f"{DATA_DIR}/{os.listdir(DATA_DIR)[0]}" + else: + print("--profile is required when you have more than one database.") + sys.exit(1) + else: + filename = build_database_filename(profile_name) + + print(filename) + if os.path.exists(filename) and os.path.isfile(filename): + engine = create_engine(f"sqlite:///{filename}") + Session = sessionmaker(bind=engine) + return Session() + else: + print(f"No database for profile '{profile_name}'. Did you run 'init'?") + sys.exit(1) diff --git a/importer.py b/importer.py index 28b5161..168050c 100644 --- a/importer.py +++ b/importer.py @@ -6,16 +6,17 @@ from datetime import datetime from sqlalchemy import create_engine, desc from sqlalchemy.orm import sessionmaker from models import Transaction +from helper import get_session import sys @click.command(name="import") @click.option("--filetype", "-t", type=click.Choice(["dkb", "sparkasse-mt940", "bunq-csv"], case_sensitive=False)) +@click.option("--profile", "-p") +@click.option("--force", default=False) @click.argument("filename", type=click.Path(exists=True)) -def command(filename, filetype): - engine = create_engine("sqlite:///app.db") - Session = sessionmaker(bind=engine) - session = Session() +def command(filename, profile, filetype): + session = get_session(profile) latest = session.query(Transaction).order_by(desc(Transaction.date)).first() new = [] @@ -30,9 +31,9 @@ def command(filename, filetype): name = data["applicant_name"] description = data["purpose"] - # if latest and data['date'] < latest.date: - # print("Found transaction older than then oldest transction in the DB. Aborting") - # sys.exit(1) + if not force and latest and data["date"] < latest.date: + print("Found transaction older than then oldest transction in the DB. Aborting") + sys.exit(1) new.append(Transaction(date=date, name=name, iban=iban, amount=amount, description=description)) print(".", end="", flush=True) @@ -41,7 +42,6 @@ def command(filename, filetype): with open(click.format_filename(filename)) as fh: fh.readline() # Get rid of first line csv_reader = csv.reader(fh, delimiter=";") - count = 0 for line in csv_reader: date = datetime.strptime(line[0], "%Y-%m-%d") amount = int(float(line[2].replace(",", "")) * 100) @@ -49,15 +49,15 @@ def command(filename, filetype): name = line[5] description = line[6] - # if latest and date < latest.date: - # print("Found transaction older than then oldest transction in the DB. Aborting") - # sys.exit(1) + if not force and latest and date < latest.date: + print("Found transaction older than then oldest transction in the DB. Aborting") + sys.exit(1) new.append(Transaction(date=date, name=name, iban=iban, amount=amount, description=description)) print(".", end="", flush=True) elif filetype == "dkb": - with open(click.format_filename(filename), 'r', encoding='utf8') as fh: + with open(click.format_filename(filename), "r", encoding="utf8") as fh: fh.readline() # Get rid of first line csv_reader = csv.reader(fh, delimiter=";") for line in csv_reader: @@ -67,9 +67,9 @@ def command(filename, filetype): name = line[3] description = line[4] - # if latest and date < latest.date: - # print("Found transaction older than then oldest transction in the DB. Aborting") - # sys.exit(1) + if not force and latest and date < latest.date: + print("Found transaction older than then oldest transction in the DB. Aborting") + sys.exit(1) new.append(Transaction(date=date, name=name, iban=iban, amount=amount, description=description)) print(".", end="", flush=True) diff --git a/serve.py b/serve.py index ab46f3c..754f38b 100644 --- a/serve.py +++ b/serve.py @@ -1,18 +1,10 @@ #! /usr/bin/env python3 from bottle import route, run, template, redirect, request, post -from sqlalchemy import create_engine -from sqlalchemy.orm import sessionmaker -from sqlalchemy.ext.declarative import declarative_base from models import Category, Transaction +from helper import get_session import click -from pprint import pprint - -engine = create_engine("sqlite:///app.db") -Base = declarative_base() - -Session = sessionmaker(bind=engine) -session = Session() +session = None @route("/") @@ -39,7 +31,7 @@ def category(name): @route("/category//delete") -def category(name): +def delete_category(name): c = session.query(Category).filter(Category.name == name).first() if c: session.delete(c) @@ -68,6 +60,10 @@ def buksort(): session.commit() return redirect("/transactions") + @click.command(name="serve") -def command(): +@click.option("--profile", "-p") +def command(profile): + global session + session = get_session(profile) run(host="localhost", port=8080, reloader=True, debug=True) diff --git a/sort.py b/sort.py index a50fa56..54bfbae 100644 --- a/sort.py +++ b/sort.py @@ -2,8 +2,9 @@ from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from sqlalchemy.ext.declarative import declarative_base -from models import * +from models import Category, Transaction from categories import add_category +from helper import get_session import sys import click @@ -12,12 +13,9 @@ from prompt_toolkit.shortcuts import prompt @click.command(name="sort") -def command(): - engine = create_engine("sqlite:///app.db") - Base = declarative_base() - - Session = sessionmaker(bind=engine) - session = Session() +@click.option("--profile", "-p") +def command(profile): + session = get_session(profile) categories = session.query(Category).all() category_lookup = {c.full_name(): c.id for c in categories}