Added support for profiles
This commit is contained in:
parent
dfea791bb6
commit
dbde36a4cb
9 changed files with 110 additions and 52 deletions
1
Pipfile
1
Pipfile
|
@ -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
10
Pipfile.lock
generated
|
@ -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": {
|
||||||
|
|
|
@ -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
32
cli
|
@ -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()
|
||||||
|
|
14
create.py
14
create.py
|
@ -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
42
helper.py
Normal 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)
|
30
importer.py
30
importer.py
|
@ -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)
|
||||||
|
|
20
serve.py
20
serve.py
|
@ -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
12
sort.py
|
@ -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}
|
||||||
|
|
Loading…
Add table
Reference in a new issue