summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRené 'Necoro' Neumann <necoro@necoro.net>2013-10-14 23:50:27 +0200
committerRené 'Necoro' Neumann <necoro@necoro.net>2013-10-14 23:50:27 +0200
commitdbb134751c3a87cf203cd243b1952b146b8914c1 (patch)
treecb34abe63bd1e3f95a4616b4a20a7528380014f1
parenta628fd3084e94959dc29c86912f2a2e97038799c (diff)
downloadkosten-dbb134751c3a87cf203cd243b1952b146b8914c1.tar.gz
kosten-dbb134751c3a87cf203cd243b1952b146b8914c1.tar.bz2
kosten-dbb134751c3a87cf203cd243b1952b146b8914c1.zip
Finish login stuff
Diffstat (limited to '')
-rw-r--r--app/__init__.py1
-rw-r--r--app/forms.py23
-rw-r--r--app/login.py14
-rw-r--r--app/model.py55
-rw-r--r--app/views/__init__.py3
-rw-r--r--app/views/categories.py6
-rw-r--r--app/views/consts.py13
-rw-r--r--app/views/expenses.py15
-rw-r--r--app/views/login.py26
-rw-r--r--templates/login/login.jinja13
-rw-r--r--templates/menu.jinja3
11 files changed, 143 insertions, 29 deletions
diff --git a/app/__init__.py b/app/__init__.py
index 0f84d3f..ca45a1d 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -10,4 +10,5 @@ app.jinja_env.autoescape = True
app.config.from_pyfile("settings.py")
from .model import db
+from .login import login_manager
from . import views
diff --git a/app/forms.py b/app/forms.py
index 8fbd4fb..182f6de 100644
--- a/app/forms.py
+++ b/app/forms.py
@@ -1,7 +1,7 @@
# -*- encoding: utf-8 -*-
from flask.ext.wtf import Form
-from wtforms.fields import DateField, IntegerField, StringField, HiddenField
+from wtforms.fields import DateField, IntegerField, StringField, HiddenField, PasswordField
from wtforms import validators as v
from wtforms import fields
@@ -10,6 +10,7 @@ from wtforms.ext.sqlalchemy.fields import QuerySelectField
import datetime
from . import app
+from .model import User
today = datetime.date.today
@@ -64,3 +65,23 @@ class ConstForm(Form):
prev = QuerySelectField(u"Vorgänger",
get_label="description",
allow_blank=True)
+
+class LoginForm(Form):
+ username = StringField(u"Username", req)
+ pwd = PasswordField(u"Passwort", req)
+
+ def __init__(self, *args, **kwargs):
+ Form.__init__(self,*args, **kwargs)
+ self.user = None
+
+ def validate(self):
+ rv = Form.validate(self)
+ if not rv:
+ return False
+
+ user = User.get_by(name = self.username.data)
+ if user is None or not user.check_password(self.pwd.data):
+ return False
+
+ self.user = user
+ return True
diff --git a/app/login.py b/app/login.py
new file mode 100644
index 0000000..6d3fdfa
--- /dev/null
+++ b/app/login.py
@@ -0,0 +1,14 @@
+from flask.ext.login import LoginManager
+
+# just for exporting
+from flask.ext.login import login_user, logout_user, login_required, current_user
+
+from passlib.apps import custom_app_context as pwd_context
+
+from . import app
+from .model import User
+
+login_manager = LoginManager()
+login_manager.init_app(app)
+
+login_manager.user_loader(User.get)
diff --git a/app/model.py b/app/model.py
index 414cfd0..ebf3d5b 100644
--- a/app/model.py
+++ b/app/model.py
@@ -58,17 +58,45 @@ class User (Model):
def check_password(self, pwd):
return pwd_context.verify(pwd, self.pwd)
-class Category (Model):
+ # Stuff needed for LoginManager
+ def is_authenticated(self):
+ return True
+
+ def is_active(self):
+ return True
+
+ def is_anonymous(self):
+ return False
+
+ def get_id(self):
+ return unicode(self.id)
+
+class UserModel (Model):
+ __abstract__ = True
+
+ @declared_attr
+ def user_id(cls):
+ return ReqColumn(db.Integer, db.ForeignKey(User.id))
+
+ @declared_attr
+ def user(cls):
+ return db.relationship('User')
+
+ @classmethod
+ def of (cls, user):
+ return cls.query.filter_by(user = user)
+
+class Category (UserModel):
name = ReqColumn(db.Unicode(50), unique = True)
- user_id = ReqColumn(db.Integer, db.ForeignKey(User.id))
parent_id = Column(db.Integer, db.ForeignKey('category.id'))
children = db.relationship('Category',
backref=db.backref('parent', remote_side="Category.id"))
- def __init__ (self, name, parent_id = None):
+ def __init__ (self, name, user, parent_id = None):
Model.__init__(self)
self.name = name
+ self.user = user
self.parent_id = parent_id
def __repr__ (self):
@@ -77,7 +105,7 @@ class Category (Model):
else:
return '<Category "%s">' % self.name
-class Expense (Model):
+class Expense (UserModel):
__abstract__ = True
description = Column(db.Unicode(50))
@@ -102,12 +130,7 @@ class SingleExpense (Expense):
@classmethod
def of_month (cls, user, month, year):
- comp = sql.and_(cls.user == user,
- sql.and_(
- cls.month == month,
- cls.year == year))
-
- return cls.query.filter(comp)
+ return cls.of(user).filter_by(month = month, year = year)
@property
def date (self):
@@ -133,9 +156,9 @@ class ConstExpense (Expense):
return to_exp(self.expense / self.months)
@classmethod
- def of_month (cls, month, year):
+ def of_month (cls, user, month, year):
d = datetime.date(year, month, 1)
- return cls.query.filter(sql.between(d, cls.start, cls.end))
+ return cls.of(user).filter(sql.between(d, cls.start, cls.end))
#
# Work entities (not stored in DB)
@@ -147,7 +170,7 @@ class CatExpense (namedtuple('CatExpense', 'cat expense exps')):
def all (self):
return self.exps.order_by(SingleExpense.day).all()
-class MonthExpense (namedtuple('MonthExpense', 'date catexps')):
+class MonthExpense (namedtuple('MonthExpense', 'user date catexps')):
def __init__ (self, *args, **kwargs):
self._consts = None
@@ -156,7 +179,7 @@ class MonthExpense (namedtuple('MonthExpense', 'date catexps')):
@property
def consts (self):
if self._consts is None:
- self._consts = ConstExpense.of_month(self.date.month, self.date.year).all()
+ self._consts = ConstExpense.of_month(self.user, self.date.month, self.date.year).all()
return self._consts
@@ -171,10 +194,10 @@ class MonthExpense (namedtuple('MonthExpense', 'date catexps')):
@property
def all (self):
- return SingleExpense.of_month(self.date.month, self.date.year).order_by(SingleExpense.day).all()
+ return SingleExpense.of_month(self.user, self.date.month, self.date.year).order_by(SingleExpense.day).all()
def __str__ (self):
- return '<MonthExpense of "%s": %s>' % (self.date, self.sum)
+ return '<MonthExpense (user: %s) of "%s": %s>' % (self.user.name, self.date, self.sum)
#
# Extra indizes have to be here
diff --git a/app/views/__init__.py b/app/views/__init__.py
index 659a568..ef7c033 100644
--- a/app/views/__init__.py
+++ b/app/views/__init__.py
@@ -33,8 +33,9 @@ def format_date(s, format="%Y/%m"):
def page_not_found (error):
return render_template("404.jinja", page = request.path), 404
-from . import categories, consts, expenses, api
+from . import categories, consts, expenses, login, api
+app.register_blueprint(login.mod)
app.register_blueprint(expenses.mod)
app.register_blueprint(consts.mod, url_prefix="/const")
app.register_blueprint(categories.mod, url_prefix="/cat")
diff --git a/app/views/categories.py b/app/views/categories.py
index 3491998..1ec7b1d 100644
--- a/app/views/categories.py
+++ b/app/views/categories.py
@@ -1,23 +1,25 @@
from ..flask_extend import Blueprint
from flask import request
+from ..login import current_user, login_required
from ..utils import templated, redirect
from ..model import db, Category
mod = Blueprint('categories', __name__)
@mod.route("/", methods=("GET", "POST"))
+@login_required
@templated()
def manage ():
if request.method == "GET":
- categories = Category.query.order_by(Category.name).all()
+ categories = Category.of(current_user).order_by(Category.name).all()
return { 'cats' : categories }
else:
for id, name in request.form.iteritems():
if id.startswith("n-"):
- db.session.add(Category(name = name))
+ db.session.add(Category(name = name, user = current_user))
else:
Category.get(id).name = name
diff --git a/app/views/consts.py b/app/views/consts.py
index dab02e0..8eef73b 100644
--- a/app/views/consts.py
+++ b/app/views/consts.py
@@ -5,6 +5,7 @@ import datetime
from sqlalchemy import sql
from ..model import db, Category, ConstExpense
+from ..login import current_user, login_required
from ..forms import ConstForm, today
from ..utils import templated, redirect
@@ -13,7 +14,7 @@ mod = Blueprint('consts', __name__)
def const_form(cur=None, obj=None):
obj = cur if obj is None else obj
form = ConstForm(obj=obj)
- form.category.query = Category.query.order_by(Category.name)
+ form.category.query = Category.of(current_user).order_by(Category.name)
# init prev_list
CE = ConstExpense
@@ -24,16 +25,17 @@ def const_form(cur=None, obj=None):
filter = sql.or_(CE.next == cur, filter)
filter = sql.and_(filter, CE.id != cur.id)
- form.prev.query = CE.query.filter(filter).order_by(CE.description)
+ form.prev.query = CE.of(current_user).filter(filter).order_by(CE.description)
return form
@mod.route("/")
+@login_required
@templated()
def list ():
d = today()
- expenses = ConstExpense.query.order_by(ConstExpense.description).all()
+ expenses = ConstExpense.of(current_user).order_by(ConstExpense.description).all()
current = []
old = []
@@ -51,11 +53,13 @@ def list ():
return { 'current': current, 'old': old, 'future': future }
@mod.route("/<int:id>")
+@login_required
@templated()
def show(id):
return { 'exp': ConstExpense.get(id) }
@mod.route("/edit/<int:id>", methods=("GET", "POST"))
+@login_required
@templated()
def edit(id):
exp = ConstExpense.get(id)
@@ -75,6 +79,7 @@ def edit(id):
return { 'form': form }
@mod.route("/add/from/<int:other>")
+@login_required
@templated(".add")
def add_from(other):
exp = ConstExpense() # needed to initialize 'CE.next'
@@ -93,6 +98,7 @@ def add_from(other):
return { 'form': form }
@mod.route("/add/", methods=("GET", "POST"))
+@login_required
@templated()
def add ():
exp = ConstExpense()
@@ -101,6 +107,7 @@ def add ():
if form.validate_on_submit():
form.populate_obj(exp)
+ exp.user = current_user
db.session.add(exp)
db.session.commit()
return redirect(".show", id = exp.id)
diff --git a/app/views/expenses.py b/app/views/expenses.py
index efa6717..aa1fd51 100644
--- a/app/views/expenses.py
+++ b/app/views/expenses.py
@@ -5,7 +5,7 @@ import datetime, decimal
from sqlalchemy import sql, func
from ..model import db, Category, SingleExpense, CatExpense, MonthExpense
-
+from ..login import login_required, current_user
from ..forms import ExpenseForm
from ..utils import templated, redirect
@@ -13,26 +13,26 @@ mod = Blueprint('expenses', __name__)
def expense_form(obj=None):
form = ExpenseForm(obj=obj)
- form.category.query = Category.query.order_by(Category.name)
+ form.category.query = Category.of(current_user).order_by(Category.name)
return form
def calc_month_exp(year, month):
ssum = func.sum(SingleExpense.expense)
- query = SingleExpense.of_month(month, year)
+ query = SingleExpense.of_month(current_user, month, year)
result = query.group_by(SingleExpense.category_id).\
values(SingleExpense.category_id, ssum)
exps = [CatExpense(Category.query.get(c), s, query.filter(SingleExpense.category_id == c)) for c,s in result]
- return MonthExpense(datetime.date(year, month, 1), exps)
+ return MonthExpense(current_user, datetime.date(year, month, 1), exps)
def pie_stuff(exp):
expenses = {}
for c in exp.catexps:
expenses[c.cat.name] = float(c.expense)
- for c in Category.query.order_by(Category.name).all():
+ for c in Category.of(current_user).order_by(Category.name).all():
yield (c.name, expenses.get(c.name, 0.0))
def calc_month_and_pie(year, month):
@@ -59,6 +59,7 @@ def is_last(exp):
return exp.date >= datetime.date.today().replace(day = 1)
@mod.route("/<int(fixed_digits=4):year>/<int(fixed_digits=2):month>")
+@login_required
@templated(".show")
def show_date(year, month):
c,p = calc_month_and_pie(year, month)
@@ -67,6 +68,7 @@ def show_date(year, month):
mod.add_url_rule("/<path:p>", endpoint = "show_date_str", build_only = True)
@mod.route("/")
+@login_required
@templated()
def show():
d = datetime.date.today()
@@ -80,6 +82,7 @@ def show():
return { 'exps' : [first, second], 'pies': [pfirst, psecond] }
@mod.route("/edit/<int:id>", methods=("GET", "POST"))
+@login_required
@templated()
def edit(id):
exp = SingleExpense.get(id)
@@ -101,6 +104,7 @@ def edit(id):
return { 'form': form }
@mod.route("/add/", methods=("GET", "POST"))
+@login_required
@templated()
def add():
form = expense_form()
@@ -109,6 +113,7 @@ def add():
exp = SingleExpense()
form.populate_obj(exp)
+ exp.user = current_user
db.session.add(exp)
db.session.commit()
diff --git a/app/views/login.py b/app/views/login.py
new file mode 100644
index 0000000..8693c30
--- /dev/null
+++ b/app/views/login.py
@@ -0,0 +1,26 @@
+from ..flask_extend import Blueprint
+from flask import request, url_for, redirect
+
+from ..forms import LoginForm
+from ..login import login_user, logout_user, login_manager
+from ..utils import templated
+
+mod = Blueprint('login', __name__)
+
+@mod.route("/login", methods=("GET", "POST"))
+@templated()
+def login():
+ form = LoginForm()
+
+ if form.validate_on_submit():
+ login_user(form.user)
+ return redirect(request.args.get("next") or url_for("index"))
+
+ return { 'form': form }
+
+@mod.route("/logout")
+def logout():
+ logout_user()
+ return redirect(url_for(".login"))
+
+login_manager.login_view = 'login.login'
diff --git a/templates/login/login.jinja b/templates/login/login.jinja
new file mode 100644
index 0000000..3254fd6
--- /dev/null
+++ b/templates/login/login.jinja
@@ -0,0 +1,13 @@
+{% extends "layout.jinja" %}
+{% from "macros.jinja" import render_form %}
+
+{% block heading %}
+ Login
+{% endblock %}
+
+{% block content %}
+ <form name="login" method="post">
+ {{ render_form(form) }}
+ <input type="submit">
+ </form>
+{% endblock %}
diff --git a/templates/menu.jinja b/templates/menu.jinja
index 9950c96..79b20af 100644
--- a/templates/menu.jinja
+++ b/templates/menu.jinja
@@ -2,6 +2,7 @@
("index", "Kosten"),
("expenses.add", "Neu"),
("consts.list", "Konstante Kosten"),
- ("categories.manage", "Kategorien")
+ ("categories.manage", "Kategorien"),
+ ("login.logout", "Logout")
]
%}