1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/ihatemoney_ynh.git synced 2024-09-03 19:26:15 +02:00

No need anymore to pass the project_id to the urls.

The project is now directly added to the context local g object, and injected on the fly into the urls that need it.

This commits also add ideas found while reading the flask documentation. The project can be enhanced in many ways, some ideas are stated there.
This commit is contained in:
Alexis Metaireau 2011-08-19 23:44:54 +02:00
parent 402d756bf1
commit 28a3abf96d
8 changed files with 93 additions and 72 deletions

7
TODO
View file

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

View file

@ -1,14 +1,14 @@
{% extends "layout.html" %}
{% block top_menu %}
<a href="{{ url_for('list_bills', project_id=project.id) }}">Back to the list</a>
<a href="{{ url_for('list_bills') }}">Back to the list</a>
{% endblock %}
{% block content %}
<h2>Add a new bill</h2>
<div class="container span-24 add-bill uniForm" style="width: 400px">
<form action="{{ url_for('add_bill', project_id=project.id) }}" method="post" class=uniForm">
<form action="{{ url_for('add_bill') }}" method="post" class=uniForm">
{{ forms.add_bill(form) }}
</form>
</div>

View file

@ -1,6 +1,6 @@
{% extends "layout.html" %}
{% block content %}
<form action="{{ url_for("add_member", project_id=project.id) }}" method="post">
<form action="{{ url_for("add_member") }}" method="post">
{{ forms.add_member(form) }}
</form>
{% endblock %}

View file

@ -1,14 +1,14 @@
{% extends "layout.html" %}
{% block top_menu %}
<a href="{{ url_for('list_bills', project_id=project.id) }}">Back to the list</a>
<a href="{{ url_for('list_bills') }}">Back to the list</a>
{% endblock %}
{% block content %}
<h2>Edit a bill</h2>
<div class="container span-24 add-bill uniForm" style="width: 400px">
<form action="{{ url_for('edit_bill', project_id=project.id, bill_id=bill_id) }}" method="post" class=uniForm">
<form action="{{ url_for('edit_bill', bill_id=bill_id) }}" method="post" class=uniForm">
{{ forms.add_bill(form) }}
</form>
</div>

View file

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

View file

@ -42,24 +42,24 @@ $('.members li').hover(function(){
{% block content %}
<div id="leftmenu" class="span-6">
<ul class="members">
{% set balance = project.get_balance() %}
{% for member in project.active_members %}
{% set balance = g.project.get_balance() %}
{% for member in g.project.active_members %}
<li class="{{ loop.cycle("even", "odd") }}">
<span class="balance {% if balance[member] > 0 %}positive{% elif balance[member] < 0 %}negative{% endif %}">{{ balance[member] }}</span>
{{ member.name }}
<a class="remove" href="{{ url_for("remove_member", project_id=project.id, member_id=member.id) }}">delete</a></li>
<a class="remove" href="{{ url_for("remove_member", member_id=member.id) }}">delete</a></li>
{% endfor %}
</ul>
<form action="{{ url_for("add_member", project_id=project.id) }}" method="post">
<form action="{{ url_for("add_member") }}" method="post">
{{ forms.add_member(member_form) }}
</form>
</div>
<div id="content" class="uniForm span-18 last">
<a id="add_bill_button" class="awesome large green button fright" href="{{ url_for('add_bill', project_id=project.id) }}">Add a bill</a>
<a id="add_bill_button" class="awesome large green button fright" href="{{ url_for('add_bill') }}">Add a bill</a>
<a id="hide_bill_form" class="awesome button fright" style="display: none;" href="#">Hide form</a>
<form id="add_bill" action="{{ url_for('add_bill', project_id=project.id) }}" method="post" style="width: 400px; display: none">{{ forms.add_bill(bill_form) }}</form>
<form id="add_bill" action="{{ url_for('add_bill') }}" method="post" style="width: 400px; display: none">{{ forms.add_bill(bill_form) }}</form>
{% if bills.count() > 0 %}
<table class="list_bills">
@ -72,15 +72,15 @@ $('.members li').hover(function(){
<td>{{ bill.what }}</td>
<td>{% for ower in bill.owers %}{{ ower.name }} {% endfor %}</td>
<td>{{ bill.amount }} ({{ bill.pay_each() }} each)</td>
<td><a href="{{ url_for("edit_bill", bill_id=bill.id, project_id=project.id) }}">edit</a>
<a href="{{ url_for("delete_bill", bill_id=bill.id, project_id=project.id) }}">delete</a></td>
<td><a href="{{ url_for("edit_bill", bill_id=bill.id) }}">edit</a>
<a href="{{ url_for("delete_bill", bill_id=bill.id) }}">delete</a></td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>Nothing to list yet. You probably want to <a href="{{ url_for("add_bill", project_id=project.id) }}">add a bill</a> ?</p>
<p>Nothing to list yet. You probably want to <a href="{{ url_for("add_bill") }}">add a bill</a> ?</p>
{% endif %}
</div>
{% endblock %}

View file

@ -3,12 +3,12 @@
<h2>Invite people to join this project</h2>
<p>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.</p>
<p>If you prefer, you can <a href="{{ url_for("list_bills", project_id=project.id) }}">skip this step</a> and notify them yourself</p>
<p>If you prefer, you can <a href="{{ url_for("list_bills") }}">skip this step</a> and notify them yourself</p>
{% include "display_errors.html" %}
<form method="post" accept-charset="utf-8">
{{ form.hidden_tag() }}
<p>{{ form.emails.label }}<br /> {{ form.emails }}</p>
<p>{{ form.submit }} <a href="{{ url_for("list_bills", project_id=project.id) }}">No, thanks</a></p>
<p>{{ form.submit }} <a href="{{ url_for("list_bills") }}">No, thanks</a></p>
</form>
{% endblock %}

View file

@ -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("/<string:project_id>/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("/<project_id>/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("/<string:project_id>/")
@requires_auth
def list_bills(project):
@app.route("/<project_id>/")
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("/<string:project_id>/members/add", methods=["GET", "POST"])
@requires_auth
def add_member(project):
@app.route("/<project_id>/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("/<string:project_id>/members/<int:member_id>/delete", methods=["GET", "POST"])
@requires_auth
def remove_member(project, member_id):
@app.route("/<project_id>/members/<member_id>/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("/<string:project_id>/add", methods=["GET", "POST"])
@requires_auth
def add_bill(project):
form = get_billform_for(project)
@app.route("/<project_id>/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("/<string:project_id>/delete/<int:bill_id>")
@requires_auth
def delete_bill(project, bill_id):
@app.route("/<project_id>/delete/<int:bill_id>")
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("/<string:project_id>/edit/<int:bill_id>", methods=["GET", "POST"])
@requires_auth
def edit_bill(project, bill_id):
@app.route("/<project_id>/edit/<int:bill_id>", 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("/<string:project_id>/compute")
@requires_auth
def compute_bills(project):
@app.route("/<project_id>/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("/<string:project_id>/reset")
@requires_auth
def reset_bills(project):
@app.route("/<project_id>/reset")
def reset_bills():
"""Reset the list of bills"""
# FIXME replace with the archive feature
# get all the bills which are not processed