Multiple importer and bulksort in the GUI
This commit is contained in:
parent
60cbf9da2e
commit
dfea791bb6
11 changed files with 133 additions and 53 deletions
2
.flake8
Normal file
2
.flake8
Normal file
|
@ -0,0 +1,2 @@
|
|||
[flake8]
|
||||
max-line-length = 120
|
1
Pipfile
1
Pipfile
|
@ -5,6 +5,7 @@ verify_ssl = true
|
|||
|
||||
[dev-packages]
|
||||
flake8 = "*"
|
||||
autopep8 = "*"
|
||||
|
||||
[packages]
|
||||
sqlalchemy = "*"
|
||||
|
|
9
Pipfile.lock
generated
9
Pipfile.lock
generated
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "25b9941c75b9e81082b3302534b779bedf7a24979213c97e7da0524201b2f333"
|
||||
"sha256": "731c3810c49b4b77cd9aac8450a97650031dc994b7367106567d351f96011a24"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
|
@ -64,6 +64,13 @@
|
|||
}
|
||||
},
|
||||
"develop": {
|
||||
"autopep8": {
|
||||
"hashes": [
|
||||
"sha256:0f592a0447acea0c2b0a9602be1e4e3d86db52badd2e3c84f0193bfd89fd3a43"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.5"
|
||||
},
|
||||
"entrypoints": {
|
||||
"hashes": [
|
||||
"sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19",
|
||||
|
|
10
README.md
Normal file
10
README.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
# schmeckels
|
||||
|
||||
## ToDo
|
||||
|
||||
- Profile via config files im home
|
||||
- Graphen
|
||||
- Linechart Verlauf über Monat (Gesamtetrag)
|
||||
- Piechart verschiedene Kategorien
|
||||
- Automatcher für Regeln
|
||||
- setup.py
|
86
importer.py
86
importer.py
|
@ -1,47 +1,79 @@
|
|||
#! /usr/bin/env python3
|
||||
import mt940
|
||||
import csv
|
||||
import click
|
||||
from datetime import datetime
|
||||
from sqlalchemy import create_engine, desc
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from models import Transaction
|
||||
import sys
|
||||
|
||||
|
||||
def convert_amount(t):
|
||||
return int(t.amount * 100)
|
||||
|
||||
@click.command(name='import')
|
||||
@click.argument('filename', type=click.Path(exists=True))
|
||||
def command(filename):
|
||||
click.echo(click.format_filename(filename))
|
||||
@click.command(name="import")
|
||||
@click.option("--filetype", "-t", type=click.Choice(["dkb", "sparkasse-mt940", "bunq-csv"], case_sensitive=False))
|
||||
@click.argument("filename", type=click.Path(exists=True))
|
||||
def command(filename, filetype):
|
||||
engine = create_engine("sqlite:///app.db")
|
||||
Base = declarative_base()
|
||||
|
||||
Session = sessionmaker(bind=engine)
|
||||
session = Session()
|
||||
|
||||
latest = session.query(Transaction).order_by(desc(Transaction.date)).first()
|
||||
|
||||
transactions = mt940.parse("2020-01.TXT")
|
||||
count = 0
|
||||
new = []
|
||||
if filetype == "sparkasse-mt940":
|
||||
transactions = mt940.parse(click.format_filename(filename))
|
||||
for t in transactions:
|
||||
data = t.data
|
||||
|
||||
date = data['date']
|
||||
amount = convert_amount(data['amount'])
|
||||
iban = data['applicant_iban']
|
||||
name = data['applicant_name']
|
||||
description = data['purpose']
|
||||
date = data["date"]
|
||||
amount = int(data["amount"].amount * 100)
|
||||
iban = data["applicant_iban"]
|
||||
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 latest and data['date'] < latest.date:
|
||||
# print("Found transaction older than then oldest transction in the DB. Aborting")
|
||||
# sys.exit(1)
|
||||
|
||||
t = Transaction(date=date, name=name, iban=iban, amount=amount, description=description)
|
||||
session.add(t)
|
||||
session.commit()
|
||||
count += 1
|
||||
new.append(Transaction(date=date, name=name, iban=iban, amount=amount, description=description))
|
||||
print(".", end="", flush=True)
|
||||
print()
|
||||
print(f"Imported {count} transactions")
|
||||
|
||||
elif filetype == "bunq-csv":
|
||||
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)
|
||||
iban = line[4]
|
||||
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)
|
||||
|
||||
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:
|
||||
fh.readline() # Get rid of first line
|
||||
csv_reader = csv.reader(fh, delimiter=";")
|
||||
for line in csv_reader:
|
||||
date = datetime.strptime(line[0], "%Y-%m-%d")
|
||||
amount = line[5]
|
||||
iban = line[4]
|
||||
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)
|
||||
|
||||
new.append(Transaction(date=date, name=name, iban=iban, amount=amount, description=description))
|
||||
print(".", end="", flush=True)
|
||||
|
||||
session.bulk_save_objects(new)
|
||||
session.commit()
|
||||
print(f"Imported {len(new)} transactions")
|
||||
|
|
|
@ -31,7 +31,6 @@ class Transaction(Base):
|
|||
return "UNKNOWN FORMAT"
|
||||
|
||||
|
||||
|
||||
class Category(Base):
|
||||
__tablename__ = "Category"
|
||||
id = Column(Integer, primary_key=True)
|
||||
|
|
46
serve.py
46
serve.py
|
@ -1,11 +1,13 @@
|
|||
#! /usr/bin/env python3
|
||||
from bottle import route, run, template, redirect
|
||||
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 *
|
||||
from models import Category, Transaction
|
||||
import click
|
||||
|
||||
from pprint import pprint
|
||||
|
||||
engine = create_engine("sqlite:///app.db")
|
||||
Base = declarative_base()
|
||||
|
||||
|
@ -13,17 +15,19 @@ Session = sessionmaker(bind=engine)
|
|||
session = Session()
|
||||
|
||||
|
||||
@route('/')
|
||||
@route("/")
|
||||
def index():
|
||||
return template('index')
|
||||
return template("index")
|
||||
|
||||
@route('/categories')
|
||||
|
||||
@route("/categories")
|
||||
def categories():
|
||||
categories = session.query(Category).all()
|
||||
categories = [c for c in categories if c.is_child()]
|
||||
return template('categories', categories=categories)
|
||||
return template("categories", categories=categories)
|
||||
|
||||
@route('/category/<name>')
|
||||
|
||||
@route("/category/<name>")
|
||||
def category(name):
|
||||
c = session.query(Category).filter(Category.name == name).first()
|
||||
if c:
|
||||
|
@ -31,9 +35,10 @@ def category(name):
|
|||
else:
|
||||
transactions = []
|
||||
|
||||
return template('category', category=c,transactions=transactions)
|
||||
return template("category", category=c, transactions=transactions)
|
||||
|
||||
@route('/category/<name>/delete')
|
||||
|
||||
@route("/category/<name>/delete")
|
||||
def category(name):
|
||||
c = session.query(Category).filter(Category.name == name).first()
|
||||
if c:
|
||||
|
@ -42,12 +47,27 @@ def category(name):
|
|||
|
||||
return redirect("/categories")
|
||||
|
||||
@route('/transactions')
|
||||
|
||||
@route("/transactions")
|
||||
def transactions():
|
||||
transactions = session.query(Transaction).all()
|
||||
return template('transactions', transactions=transactions)
|
||||
categories = session.query(Category).all()
|
||||
childs = [c for c in categories if c.is_child()]
|
||||
return template("transactions", transactions=transactions, categories=childs)
|
||||
|
||||
|
||||
@click.command(name='serve')
|
||||
@post("/bulksort")
|
||||
def buksort():
|
||||
cid = request.forms.get("category")
|
||||
transaction_ids = request.forms.getall("transaction")
|
||||
for id in transaction_ids:
|
||||
t = session.query(Transaction).get(id)
|
||||
t.category__id = cid
|
||||
session.add(t)
|
||||
print(f"Sorted {len(transaction_ids)} transactions into category {cid}")
|
||||
session.commit()
|
||||
return redirect("/transactions")
|
||||
|
||||
@click.command(name="serve")
|
||||
def command():
|
||||
run(host='localhost', port=8080, reloader=True,debug=True)
|
||||
run(host="localhost", port=8080, reloader=True, debug=True)
|
||||
|
|
4
sort.py
4
sort.py
|
@ -11,7 +11,7 @@ from prompt_toolkit.completion import FuzzyWordCompleter
|
|||
from prompt_toolkit.shortcuts import prompt
|
||||
|
||||
|
||||
@click.command(name='sort')
|
||||
@click.command(name="sort")
|
||||
def command():
|
||||
engine = create_engine("sqlite:///app.db")
|
||||
Base = declarative_base()
|
||||
|
@ -45,5 +45,3 @@ def command():
|
|||
add_category(select)
|
||||
|
||||
print("-" * 20)
|
||||
|
||||
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
% rebase("base.tpl")
|
||||
|
||||
<form action="/bulksort" method="POST">
|
||||
<select name="category">
|
||||
% for c in categories:
|
||||
<option value="{{ c.id }}">{{ c.full_name() }}</option>
|
||||
% end
|
||||
</select>
|
||||
<input type="submit">
|
||||
<table class="table-auto">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="px-4 py-2"></th>
|
||||
<th class="px-4 py-2">Date</th>
|
||||
<th class="px-4 py-2">Sender/Empfänger</th>
|
||||
<th class="px-4 py-2">Verwendungszweck</th>
|
||||
|
@ -13,6 +21,7 @@
|
|||
<tbody>
|
||||
% for t in transactions:
|
||||
<tr>
|
||||
<td class="border px-4 py-2"><input type="checkbox" name="transaction" value="{{ t.id }}"></td>
|
||||
<td class="border px-4 py-2">{{ t.get_date("de") }}</td>
|
||||
<td class="border px-4 py-2">{{ t.name }}</td>
|
||||
<td class="border px-4 py-2">{{ t.description }}</td>
|
||||
|
@ -26,3 +35,5 @@
|
|||
% end
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</form>
|
||||
|
|
Loading…
Add table
Reference in a new issue