diff --git a/TODO b/TODO
index e69de29..a759652 100644
--- a/TODO
+++ b/TODO
@@ -0,0 +1,7 @@
+* use flask.instance_path to get/store configuration. See http://flask.pocoo.org/docs/config/#instance-folders
+* Attach the current projec to g and modify the url_for to use it. http://flask.pocoo.org/docs/patterns/urlprocessors/
+* Use class based views to factorize the code if there is some code to factorize, see http://flask.pocoo.org/docs/views/
+* Use request.args.get('next') to redirect when authenticating
+* Move the flask app to __init__.py (http://flask.pocoo.org/docs/patterns/packages/)
+* Eventually move the url definition into a specific section
+* Render templates automatically using a decorator. see http://stackoverflow.com/questions/7054099/using-flask-blueprint-for-some-static-pages/7056374#7056374
diff --git a/budget/templates/add_bill.html b/budget/templates/add_bill.html
index d844b6a..5b3a768 100644
--- a/budget/templates/add_bill.html
+++ b/budget/templates/add_bill.html
@@ -1,14 +1,14 @@
{% extends "layout.html" %}
{% block top_menu %}
-Back to the list
+Back to the list
{% endblock %}
{% block content %}
Add a new bill
-
diff --git a/budget/templates/add_member.html b/budget/templates/add_member.html
index 8d24aee..5739791 100644
--- a/budget/templates/add_member.html
+++ b/budget/templates/add_member.html
@@ -1,6 +1,6 @@
{% extends "layout.html" %}
{% block content %}
-
{% endblock %}
diff --git a/budget/templates/edit_bill.html b/budget/templates/edit_bill.html
index c78eb09..03a1a26 100644
--- a/budget/templates/edit_bill.html
+++ b/budget/templates/edit_bill.html
@@ -1,14 +1,14 @@
{% extends "layout.html" %}
{% block top_menu %}
-Back to the list
+Back to the list
{% endblock %}
{% block content %}
Edit a bill
-
diff --git a/budget/templates/invitation_mail b/budget/templates/invitation_mail
index 53991ed..83a7840 100644
--- a/budget/templates/invitation_mail
+++ b/budget/templates/invitation_mail
@@ -1,10 +1,10 @@
Hi,
-Someone using the email adress {{ email }} invited you to share your expenses for {{ project.name }} on our application.
+Someone using the email adress {{ g.project.contact_email }} invited you to share your expenses for {{ g.project.name }} on our application.
It's as simple as saying what did you paid for, for who, and how much did it cost you, we are caring about the rest.
-You can access it here: {{ SITE_URL }}{{ url_for("list_bills", project_id=project.id) }}, the password is "{{ project.password }}".
+You can access it here: {{ SITE_URL }}{{ url_for("list_bills") }}, the password is "{{ g.project.password }}".
Enjoy,
Some weird guys
diff --git a/budget/templates/list_bills.html b/budget/templates/list_bills.html
index fe564e1..94b0f8f 100644
--- a/budget/templates/list_bills.html
+++ b/budget/templates/list_bills.html
@@ -42,24 +42,24 @@ $('.members li').hover(function(){
{% block content %}
-
Add a bill
+
Add a bill
Hide form
-
+
{% if bills.count() > 0 %}
@@ -72,15 +72,15 @@ $('.members li').hover(function(){
{{ bill.what }} |
{% for ower in bill.owers %}{{ ower.name }} {% endfor %} |
{{ bill.amount }} ({{ bill.pay_each() }} each) |
- edit
- delete |
+ edit
+ delete |
{% endfor %}
{% else %}
-
Nothing to list yet. You probably want to add a bill ?
+
Nothing to list yet. You probably want to add a bill ?
{% endif %}
{% endblock %}
diff --git a/budget/templates/send_invites.html b/budget/templates/send_invites.html
index f618803..fa46c81 100644
--- a/budget/templates/send_invites.html
+++ b/budget/templates/send_invites.html
@@ -3,12 +3,12 @@
Invite people to join this project
Specify a (coma separated) list of email adresses you want to notify about the
creation of this budget management project and we will send them an email for you.
-If you prefer, you can skip this step and notify them yourself
+If you prefer, you can skip this step and notify them yourself
{% include "display_errors.html" %}
{% endblock %}
diff --git a/budget/web.py b/budget/web.py
index e952109..14e058c 100644
--- a/budget/web.py
+++ b/budget/web.py
@@ -1,8 +1,8 @@
from collections import defaultdict
-from flask import (Flask, session, request, redirect, url_for, render_template,
- flash)
+from flask import *
from flaskext.mail import Mail, Message
+from werkzeug.routing import RequestRedirect
# local modules
from models import db, Project, Person, Bill
@@ -35,8 +35,6 @@ def authenticate(redirect_url=None):
form = AuthenticationForm()
project_id = form.id.data
-
- redirect_url = redirect_url or url_for("list_bills", project_id=project_id)
project = Project.query.get(project_id)
create_project = False # We don't want to create the project by default
if not project:
@@ -47,6 +45,7 @@ def authenticate(redirect_url=None):
else:
# if credentials are already in session, redirect
if project_id in session and project.password == session[project_id]:
+ redirect_url = redirect_url or url_for("list_bills")
return redirect(redirect_url)
# else process the form
@@ -62,6 +61,8 @@ def authenticate(redirect_url=None):
session["projects"].insert(0, (project_id, project.name))
session[project_id] = form.password.data
session.update()
+ setattr(g, 'project', project)
+ redirect_url = redirect_url or url_for("list_bills")
return redirect(redirect_url)
return render_template("authenticate.html", form=form,
@@ -95,9 +96,32 @@ def exit():
session.clear()
return redirect(url_for("home"))
-@app.route("//invite", methods=["GET", "POST"])
-@requires_auth
-def invite(project):
+@app.url_defaults
+def add_project_id(endpoint, values):
+ if 'project_id' in values or not hasattr(g, 'project'):
+ return
+ if app.url_map.is_endpoint_expecting(endpoint, 'project_id'):
+ values['project_id'] = g.project.id
+
+@app.url_value_preprocessor
+def pull_project(endpoint, values):
+ if not values:
+ values = {}
+ project_id = values.pop('project_id', None)
+ if project_id:
+ project = Project.query.get(project_id)
+ if not project:
+ raise RequestRedirect(url_for("create_project"))
+ if project.id in session and session[project.id] == project.password:
+ # add project into kwargs and call the original function
+ g.project = project
+ else:
+ # redirect to authentication page
+ raise RequestRedirect(
+ url_for("authenticate", redirect_url=request.url))
+
+@app.route("//invite", methods=["GET", "POST"])
+def invite():
form = InviteForm()
@@ -105,51 +129,46 @@ def invite(project):
if form.validate():
# send the email
- message_body = render_template("invitation_mail",
- email=project.contact_email, project=project)
+ message_body = render_template("invitation_mail")
message_title = "You have been invited to share your"\
- + " expenses for %s" % project.name
+ + " expenses for %s" % g.project.name
msg = Message(message_title,
body=message_body,
recipients=[email.strip()
for email in form.emails.data.split(",")])
mail.send(msg)
- return redirect(url_for("list_bills", project_id=project.id))
+ return redirect(url_for("list_bills"))
- return render_template("send_invites.html", form=form, project=project)
+ return render_template("send_invites.html", form=form)
-@app.route("//")
-@requires_auth
-def list_bills(project):
+@app.route("//")
+def list_bills():
bills = Bill.query.join(Person, Project)\
.filter(Bill.payer_id == Person.id)\
.filter(Person.project_id == Project.id)\
- .filter(Project.id == project.id)\
+ .filter(Project.id == g.project.id)\
.order_by(Bill.date.desc())
return render_template("list_bills.html",
- bills=bills, project=project,
- member_form=MemberForm(project),
- bill_form=get_billform_for(project)
+ bills=bills, member_form=MemberForm(g.project),
+ bill_form=get_billform_for(g.project)
)
-@app.route("//members/add", methods=["GET", "POST"])
-@requires_auth
-def add_member(project):
+@app.route("//members/add", methods=["GET", "POST"])
+def add_member():
# FIXME manage form errors on the list_bills page
- form = MemberForm(project)
+ form = MemberForm(g.project)
if request.method == "POST":
if form.validate():
- db.session.add(Person(name=form.name.data, project=project))
+ db.session.add(Person(name=form.name.data, project=g.project))
db.session.commit()
- return redirect(url_for("list_bills", project_id=project.id))
- return render_template("add_member.html", form=form, project=project)
+ return redirect(url_for("list_bills"))
+ return render_template("add_member.html", form=form)
-@app.route("//members//delete", methods=["GET", "POST"])
-@requires_auth
-def remove_member(project, member_id):
+@app.route("//members//delete", methods=["GET", "POST"])
+def remove_member(member_id):
person = Person.query.get_or_404(member_id)
- if person.project == project:
+ if person.project == g.project:
if not person.has_bills():
db.session.delete(person)
db.session.commit()
@@ -158,12 +177,11 @@ def remove_member(project, member_id):
person.activated = False
db.session.commit()
flash("User '%s' has been desactivated" % person.name)
- return redirect(url_for("list_bills", project_id=project.id))
+ return redirect(url_for("list_bills"))
-@app.route("//add", methods=["GET", "POST"])
-@requires_auth
-def add_bill(project):
- form = get_billform_for(project)
+@app.route("//add", methods=["GET", "POST"])
+def add_bill():
+ form = get_billform_for(g.project)
if request.method == 'POST':
if form.validate():
bill = Bill()
@@ -171,47 +189,43 @@ def add_bill(project):
db.session.commit()
flash("The bill has been added")
- return redirect(url_for('list_bills', project_id=project.id))
+ return redirect(url_for('list_bills'))
- return render_template("add_bill.html", form=form, project=project)
+ return render_template("add_bill.html", form=form)
-@app.route("//delete/")
-@requires_auth
-def delete_bill(project, bill_id):
+@app.route("//delete/")
+def delete_bill(bill_id):
bill = Bill.query.get_or_404(bill_id)
db.session.delete(bill)
db.session.commit()
flash("The bill has been deleted")
- return redirect(url_for('list_bills', project_id=project.id))
+ return redirect(url_for('list_bills'))
-@app.route("//edit/", methods=["GET", "POST"])
-@requires_auth
-def edit_bill(project, bill_id):
+@app.route("//edit/", methods=["GET", "POST"])
+def edit_bill(bill_id):
bill = Bill.query.get_or_404(bill_id)
- form = get_billform_for(project, set_default=False)
+ form = get_billform_for(g.project, set_default=False)
if request.method == 'POST' and form.validate():
form.save(bill)
db.session.commit()
flash("The bill has been modified")
- return redirect(url_for('list_bills', project_id=project.id))
+ return redirect(url_for('list_bills'))
form.fill(bill)
- return render_template("edit_bill.html", form=form, project=project, bill_id=bill_id)
+ return render_template("edit_bill.html", form=form, bill_id=bill_id)
-@app.route("//compute")
-@requires_auth
-def compute_bills(project):
+@app.route("//compute")
+def compute_bills():
"""Compute the sum each one have to pay to each other and display it"""
- return render_template("compute_bills.html", project=project)
+ return render_template("compute_bills.html")
-@app.route("//reset")
-@requires_auth
-def reset_bills(project):
+@app.route("//reset")
+def reset_bills():
"""Reset the list of bills"""
# FIXME replace with the archive feature
# get all the bills which are not processed