Added support for profiles

This commit is contained in:
fleaz 2020-03-18 23:33:54 +01:00
parent dfea791bb6
commit dbde36a4cb
9 changed files with 110 additions and 52 deletions

View file

@ -13,6 +13,7 @@ prompt-toolkit = "*"
bottle = "*" bottle = "*"
mt-940 = "*" mt-940 = "*"
click = "*" click = "*"
xdg = "*"
[requires] [requires]
python_version = "3.8" python_version = "3.8"

10
Pipfile.lock generated
View file

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "731c3810c49b4b77cd9aac8450a97650031dc994b7367106567d351f96011a24" "sha256": "45bd6b24d71ce0251c64ad6cad358882053f9cacc52bb418fce22889da6ff702"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@ -61,6 +61,14 @@
"sha256:f28b3e8a6483e5d49e7f8949ac1a78314e740333ae305b4ba5defd3e74fb37a8" "sha256:f28b3e8a6483e5d49e7f8949ac1a78314e740333ae305b4ba5defd3e74fb37a8"
], ],
"version": "==0.1.8" "version": "==0.1.8"
},
"xdg": {
"hashes": [
"sha256:bf9032b027e3061d38c362a21b14dcf057a5b5a4906956f8e8278cefdf73f38b",
"sha256:c939c99def394cbaf765a3ee55efd6ea7e4c5eaed8d9ebc2d03af84ba35dec57"
],
"index": "pypi",
"version": "==4.0.1"
} }
}, },
"develop": { "develop": {

View file

@ -2,7 +2,6 @@
## ToDo ## ToDo
- Profile via config files im home
- Graphen - Graphen
- Linechart Verlauf über Monat (Gesamtetrag) - Linechart Verlauf über Monat (Gesamtetrag)
- Piechart verschiedene Kategorien - Piechart verschiedene Kategorien

32
cli
View file

@ -1,13 +1,41 @@
#! /usr/bin/env python3 #! /usr/bin/env python3
import click 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() @click.group()
def cli(): def cli():
pass 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__': if __name__ == '__main__':
create_dirs()
cli.add_command(sort.command) cli.add_command(sort.command)
cli.add_command(importer.command) cli.add_command(importer.command)
cli.add_command(serve.command) cli.add_command(serve.command)
cli() cli()

View file

@ -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)

42
helper.py Normal file
View file

@ -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)

View file

@ -6,16 +6,17 @@ from datetime import datetime
from sqlalchemy import create_engine, desc from sqlalchemy import create_engine, desc
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
from models import Transaction from models import Transaction
from helper import get_session
import sys import sys
@click.command(name="import") @click.command(name="import")
@click.option("--filetype", "-t", type=click.Choice(["dkb", "sparkasse-mt940", "bunq-csv"], case_sensitive=False)) @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)) @click.argument("filename", type=click.Path(exists=True))
def command(filename, filetype): def command(filename, profile, filetype):
engine = create_engine("sqlite:///app.db") session = get_session(profile)
Session = sessionmaker(bind=engine)
session = Session()
latest = session.query(Transaction).order_by(desc(Transaction.date)).first() latest = session.query(Transaction).order_by(desc(Transaction.date)).first()
new = [] new = []
@ -30,9 +31,9 @@ def command(filename, filetype):
name = data["applicant_name"] name = data["applicant_name"]
description = data["purpose"] description = data["purpose"]
# if latest and data['date'] < latest.date: if not force and latest and data["date"] < latest.date:
# print("Found transaction older than then oldest transction in the DB. Aborting") print("Found transaction older than then oldest transction in the DB. Aborting")
# sys.exit(1) sys.exit(1)
new.append(Transaction(date=date, name=name, iban=iban, amount=amount, description=description)) new.append(Transaction(date=date, name=name, iban=iban, amount=amount, description=description))
print(".", end="", flush=True) print(".", end="", flush=True)
@ -41,7 +42,6 @@ def command(filename, filetype):
with open(click.format_filename(filename)) as fh: with open(click.format_filename(filename)) as fh:
fh.readline() # Get rid of first line fh.readline() # Get rid of first line
csv_reader = csv.reader(fh, delimiter=";") csv_reader = csv.reader(fh, delimiter=";")
count = 0
for line in csv_reader: for line in csv_reader:
date = datetime.strptime(line[0], "%Y-%m-%d") date = datetime.strptime(line[0], "%Y-%m-%d")
amount = int(float(line[2].replace(",", "")) * 100) amount = int(float(line[2].replace(",", "")) * 100)
@ -49,15 +49,15 @@ def command(filename, filetype):
name = line[5] name = line[5]
description = line[6] description = line[6]
# if latest and date < latest.date: if not force and latest and date < latest.date:
# print("Found transaction older than then oldest transction in the DB. Aborting") print("Found transaction older than then oldest transction in the DB. Aborting")
# sys.exit(1) sys.exit(1)
new.append(Transaction(date=date, name=name, iban=iban, amount=amount, description=description)) new.append(Transaction(date=date, name=name, iban=iban, amount=amount, description=description))
print(".", end="", flush=True) print(".", end="", flush=True)
elif filetype == "dkb": 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 fh.readline() # Get rid of first line
csv_reader = csv.reader(fh, delimiter=";") csv_reader = csv.reader(fh, delimiter=";")
for line in csv_reader: for line in csv_reader:
@ -67,9 +67,9 @@ def command(filename, filetype):
name = line[3] name = line[3]
description = line[4] description = line[4]
# if latest and date < latest.date: if not force and latest and date < latest.date:
# print("Found transaction older than then oldest transction in the DB. Aborting") print("Found transaction older than then oldest transction in the DB. Aborting")
# sys.exit(1) sys.exit(1)
new.append(Transaction(date=date, name=name, iban=iban, amount=amount, description=description)) new.append(Transaction(date=date, name=name, iban=iban, amount=amount, description=description))
print(".", end="", flush=True) print(".", end="", flush=True)

View file

@ -1,18 +1,10 @@
#! /usr/bin/env python3 #! /usr/bin/env python3
from bottle import route, run, template, redirect, request, post 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 models import Category, Transaction
from helper import get_session
import click import click
from pprint import pprint session = None
engine = create_engine("sqlite:///app.db")
Base = declarative_base()
Session = sessionmaker(bind=engine)
session = Session()
@route("/") @route("/")
@ -39,7 +31,7 @@ def category(name):
@route("/category/<name>/delete") @route("/category/<name>/delete")
def category(name): def delete_category(name):
c = session.query(Category).filter(Category.name == name).first() c = session.query(Category).filter(Category.name == name).first()
if c: if c:
session.delete(c) session.delete(c)
@ -68,6 +60,10 @@ def buksort():
session.commit() session.commit()
return redirect("/transactions") return redirect("/transactions")
@click.command(name="serve") @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) run(host="localhost", port=8080, reloader=True, debug=True)

12
sort.py
View file

@ -2,8 +2,9 @@
from sqlalchemy import create_engine from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.declarative import declarative_base
from models import * from models import Category, Transaction
from categories import add_category from categories import add_category
from helper import get_session
import sys import sys
import click import click
@ -12,12 +13,9 @@ from prompt_toolkit.shortcuts import prompt
@click.command(name="sort") @click.command(name="sort")
def command(): @click.option("--profile", "-p")
engine = create_engine("sqlite:///app.db") def command(profile):
Base = declarative_base() session = get_session(profile)
Session = sessionmaker(bind=engine)
session = Session()
categories = session.query(Category).all() categories = session.query(Category).all()
category_lookup = {c.full_name(): c.id for c in categories} category_lookup = {c.full_name(): c.id for c in categories}