mirror of
https://github.com/YunoHost-Apps/ihatemoney_ynh.git
synced 2024-09-03 19:26:15 +02:00
Refactor the application to use blueprints.
This allows to isolate some behavior in the context of the web application so the API and the web application can behave in different ways.
This commit is contained in:
parent
45dc6edacb
commit
ef353d643c
16 changed files with 109 additions and 97 deletions
1
AUTHORS
Normal file
1
AUTHORS
Normal file
|
@ -0,0 +1 @@
|
||||||
|
The project has been started by Alexis Métaireau and Frédéric Sureau. Friends arehelping since that in the person of Arnaud Bos and Quentin Roy.
|
|
@ -1,10 +1,6 @@
|
||||||
Budget-manager
|
Budget-manager
|
||||||
##############
|
##############
|
||||||
|
|
||||||
:author: Alexis Métaireau, Frédéric Sureau
|
|
||||||
:date: 10/03/2010
|
|
||||||
:technologies: Python, Flask, SQLAlchemy, WTForm
|
|
||||||
|
|
||||||
This is a really tiny app to ease the shared houses budget management. Keep
|
This is a really tiny app to ease the shared houses budget management. Keep
|
||||||
track of who bought what, when, and for who to then compute the balance of each
|
track of who bought what, when, and for who to then compute the balance of each
|
||||||
person.
|
person.
|
||||||
|
@ -18,7 +14,7 @@ To make it run, you just have to do something like::
|
||||||
$ source venv/bin/activate
|
$ source venv/bin/activate
|
||||||
$ pip install -r requirements.txt
|
$ pip install -r requirements.txt
|
||||||
$ cd budget
|
$ cd budget
|
||||||
$ python web.py
|
$ python run.py
|
||||||
|
|
||||||
Deploy it
|
Deploy it
|
||||||
=========
|
=========
|
||||||
|
|
22
budget/run.py
Normal file
22
budget/run.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
from web import main, db, mail
|
||||||
|
import api
|
||||||
|
|
||||||
|
from flask import *
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config.from_object("default_settings")
|
||||||
|
app.register_blueprint(main)
|
||||||
|
|
||||||
|
# db
|
||||||
|
db.init_app(app)
|
||||||
|
db.app = app
|
||||||
|
db.create_all()
|
||||||
|
|
||||||
|
# mail
|
||||||
|
mail.init_app(app)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
app.run(host="0.0.0.0", debug=True)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
|
@ -1,7 +1,7 @@
|
||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
|
|
||||||
{% block top_menu %}
|
{% block top_menu %}
|
||||||
<a href="{{ url_for('list_bills') }}">Back to the list</a>
|
<a href="{{ url_for(".list_bills") }}">Back to the list</a>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<form action="{{ url_for("add_member") }}" method="post">
|
<form action="{{ url_for(".add_member") }}" method="post">
|
||||||
{{ forms.add_member(form) }}
|
{{ forms.add_member(form) }}
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
{% if create_project %}
|
{% if create_project %}
|
||||||
<p class="info">The project you are trying to access do not exist, do you want
|
<p class="info">The project you are trying to access do not exist, do you want
|
||||||
to <a href="{{ url_for("create_project", project_id=create_project) }}">create it</a>?
|
to <a href="{{ url_for(".create_project", project_id=create_project) }}">create it</a>?
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<form action="" method="POST" accept-charset="utf-8">
|
<form action="" method="POST" accept-charset="utf-8">
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
|
|
||||||
{% block top_menu %}
|
{% block top_menu %}
|
||||||
<a href="{{ url_for('list_bills') }}">Back to the list</a>
|
<a href="{{ url_for(".list_bills") }}">Back to the list</a>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
|
@ -80,7 +80,7 @@
|
||||||
{{ input(form.emails) }}
|
{{ input(form.emails) }}
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button class="btn">Send the invitations</button>
|
<button class="btn">Send the invitations</button>
|
||||||
<a href="{{ url_for("list_bills") }}">No, thanks</a>
|
<a href="{{ url_for(".list_bills") }}">No, thanks</a>
|
||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<div id="header">
|
<div id="header">
|
||||||
<div class="slide">
|
<div class="slide">
|
||||||
<h1><span>Manage your shared <br>expenses, easily</span></h1>
|
<h1><span>Manage your shared <br>expenses, easily</span></h1>
|
||||||
<a href="{{ url_for("demo") }}" class="about_link">Try out the demo</a>
|
<a href="{{ url_for(".demo") }}" class="about_link">Try out the demo</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="additional-content">
|
<div class="additional-content">
|
||||||
<p>You're sharing a house?<br /> Going on holidays with friends?<br /> Simply sharing money with others? <br /><strong>We can help!</strong></p>
|
<p>You're sharing a house?<br /> Going on holidays with friends?<br /> Simply sharing money with others? <br /><strong>We can help!</strong></p>
|
||||||
|
@ -22,14 +22,14 @@
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="span8 columns">
|
<div class="span8 columns">
|
||||||
<form action="{{ url_for('authenticate') }}" method="post">
|
<form action="{{ url_for(".authenticate") }}" method="post">
|
||||||
<h3>Log to an existing project...</h3>
|
<h3>Log to an existing project...</h3>
|
||||||
{{ forms.authenticate(auth_form, home=True) }}
|
{{ forms.authenticate(auth_form, home=True) }}
|
||||||
<button class="btn">log in</button>
|
<button class="btn">log in</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="span8 columns">
|
<div class="span8 columns">
|
||||||
<form class="create" action="{{ url_for('create_project') }}" method="post">
|
<form class="create" action="{{ url_for(".create_project") }}" method="post">
|
||||||
<h3>...or create a new one</h3>
|
<h3>...or create a new one</h3>
|
||||||
{{ forms.create_project(project_form, home=True) }}
|
{{ forms.create_project(project_form, home=True) }}
|
||||||
<button class="btn">let's get started</button>
|
<button class="btn">let's get started</button>
|
||||||
|
|
|
@ -4,7 +4,7 @@ Someone using the email adress {{ g.project.contact_email }} invited you to shar
|
||||||
|
|
||||||
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.
|
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: {{ config['SITE_URL'] }}{{ url_for("list_bills") }}, the password is "{{ g.project.password }}".
|
You can access it here: {{ config['SITE_URL'] }}{{ url_for(".list_bills") }}, the password is "{{ g.project.password }}".
|
||||||
|
|
||||||
Enjoy,
|
Enjoy,
|
||||||
Some weird guys
|
Some weird guys
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<head>
|
<head>
|
||||||
<title>Account manager</title>
|
<title>Account manager</title>
|
||||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||||
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='main.css') }}">
|
<link rel=stylesheet type=text/css href="{{ url_for("static", filename='main.css') }}">
|
||||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
|
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
|
||||||
<script type="text/javascript" charset="utf-8">
|
<script type="text/javascript" charset="utf-8">
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div class="topbar">
|
<div class="topbar">
|
||||||
<h3><a class="logo" href="{% if g.project %}{{ url_for("list_bills") }}{% endif %}">#! money?</a></h3>
|
<h3><a class="logo" href="{% if g.project %}{{ url_for(".list_bills") }}{% endif %}">#! money?</a></h3>
|
||||||
{% if g.project %}
|
{% if g.project %}
|
||||||
<ul>
|
<ul>
|
||||||
<li class="active"><a href="">Bills</a></li>
|
<li class="active"><a href="">Bills</a></li>
|
||||||
|
@ -56,12 +56,12 @@
|
||||||
<li class="divider"></li>
|
<li class="divider"></li>
|
||||||
{% for id, name in session['projects'] %}
|
{% for id, name in session['projects'] %}
|
||||||
{% if id != g.project.id %}
|
{% if id != g.project.id %}
|
||||||
<li><a href="{{ url_for("list_bills", project_id=id) }}">switch to {{ name }}</a></li>
|
<li><a href="{{ url_for(".list_bills", project_id=id) }}">switch to {{ name }}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<li><a href="{{ url_for("create_project") }}">Start a new project</a></li>
|
<li><a href="{{ url_for(".create_project") }}">Start a new project</a></li>
|
||||||
<li class="divider"></li>
|
<li class="divider"></li>
|
||||||
<li><a href="{{ url_for("exit") }}">Logout</a></li>
|
<li><a href="{{ url_for(".exit") }}">Logout</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -48,20 +48,20 @@
|
||||||
<td class="{% if balance[member] > 0 %}positive{% elif balance[member] < 0 %}negative{% endif %}">
|
<td class="{% if balance[member] > 0 %}positive{% elif balance[member] < 0 %}negative{% endif %}">
|
||||||
{% if balance[member] > 0 %}+{% endif %}{{ balance[member] }}
|
{% if balance[member] > 0 %}+{% endif %}{{ balance[member] }}
|
||||||
</td>
|
</td>
|
||||||
<td> {% if member.activated %}<a class="remove" href="{{ url_for("remove_member", member_id=member.id) }}">delete</a>{% else %}<a href="{{ url_for("reactivate", member_id=member.id) }}">reactivate</a>{% endif %}</td>
|
<td> {% if member.activated %}<a class="remove" href="{{ url_for(".remove_member", member_id=member.id) }}">delete</a>{% else %}<a href="{{ url_for(".reactivate", member_id=member.id) }}">reactivate</a>{% endif %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<form action="{{ url_for("add_member") }}" method="post">
|
<form action="{{ url_for(".add_member") }}" method="post">
|
||||||
{{ forms.add_member(member_form) }}
|
{{ forms.add_member(member_form) }}
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<a id="new-bill" href="{{ url_for('add_bill') }}" class="primary">Add a new bill</a>
|
<a id="new-bill" href="{{ url_for(".add_bill") }}" class="primary">Add a new bill</a>
|
||||||
<form id="bill-form" action="{{ url_for('add_bill') }}" method="post" style="display: none">
|
<form id="bill-form" action="{{ url_for(".add_bill") }}" method="post" style="display: none">
|
||||||
<a id="hide-bill-form" href="#">hide this form</a>
|
<a id="hide-bill-form" href="#">hide this form</a>
|
||||||
{{ forms.add_bill(bill_form) }}
|
{{ forms.add_bill(bill_form) }}
|
||||||
</form>
|
</form>
|
||||||
|
@ -77,15 +77,15 @@
|
||||||
<td>{{ bill.what }}</td>
|
<td>{{ bill.what }}</td>
|
||||||
<td>{% for ower in bill.owers %}{{ ower.name }} {% endfor %}</td>
|
<td>{% for ower in bill.owers %}{{ ower.name }} {% endfor %}</td>
|
||||||
<td>{{ bill.amount }} ({{ bill.pay_each() }} each)</td>
|
<td>{{ bill.amount }} ({{ bill.pay_each() }} each)</td>
|
||||||
<td><a href="{{ url_for("edit_bill", bill_id=bill.id) }}">edit</a>
|
<td><a href="{{ url_for(".edit_bill", bill_id=bill.id) }}">edit</a>
|
||||||
<a class="delete" href="{{ url_for("delete_bill", bill_id=bill.id) }}">delete</a></td>
|
<a class="delete" href="{{ url_for(".delete_bill", bill_id=bill.id) }}">delete</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>Nothing to list yet. You probably want to <a id="empty-new-bill" href="{{ url_for("add_bill") }}">add a bill</a> ?</p>
|
<p>Nothing to list yet. You probably want to <a id="empty-new-bill" href="{{ url_for(".add_bill") }}">add a bill</a> ?</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -4,14 +4,14 @@
|
||||||
<ol>
|
<ol>
|
||||||
<li>Create the project</li>
|
<li>Create the project</li>
|
||||||
<li><strong>Invite people</strong></li>
|
<li><strong>Invite people</strong></li>
|
||||||
<li><a href="{{ url_for("list_bills") }}">Use it!</a></li>
|
<li><a href="{{ url_for(".list_bills") }}">Use it!</a></li>
|
||||||
</ol>
|
</ol>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Invite people to join this project</h2>
|
<h2>Invite people to join this project</h2>
|
||||||
<p>Specify a (coma separated) list of email adresses you want to notify about the
|
<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>
|
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") }}">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" %}
|
{% include "display_errors.html" %}
|
||||||
<form class="invites" method="post" accept-charset="utf-8">
|
<form class="invites" method="post" accept-charset="utf-8">
|
||||||
|
|
|
@ -5,22 +5,22 @@ import unittest
|
||||||
|
|
||||||
from flask import session
|
from flask import session
|
||||||
|
|
||||||
import web
|
import run
|
||||||
import models
|
import models
|
||||||
|
|
||||||
class TestCase(unittest.TestCase):
|
class TestCase(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
web.app.config['TESTING'] = True
|
run.app.config['TESTING'] = True
|
||||||
|
|
||||||
web.app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:///memory"
|
run.app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:///memory"
|
||||||
web.app.config['CSRF_ENABLED'] = False # simplify the tests
|
run.app.config['CSRF_ENABLED'] = False # simplify the tests
|
||||||
self.app = web.app.test_client()
|
self.app = run.app.test_client()
|
||||||
|
|
||||||
models.db.init_app(web.app)
|
models.db.init_app(run.app)
|
||||||
web.mail.init_app(web.app)
|
run.mail.init_app(run.app)
|
||||||
|
|
||||||
models.db.app = web.app
|
models.db.app = run.app
|
||||||
models.db.create_all()
|
models.db.create_all()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
|
@ -57,7 +57,7 @@ class BudgetTestCase(TestCase):
|
||||||
are checked properly.
|
are checked properly.
|
||||||
"""
|
"""
|
||||||
# sending a message to one person
|
# sending a message to one person
|
||||||
with web.mail.record_messages() as outbox:
|
with run.mail.record_messages() as outbox:
|
||||||
|
|
||||||
# create a project
|
# create a project
|
||||||
self.login("raclette")
|
self.login("raclette")
|
||||||
|
@ -70,7 +70,7 @@ class BudgetTestCase(TestCase):
|
||||||
self.assertEqual(outbox[0].recipients, ["alexis@notmyidea.org"])
|
self.assertEqual(outbox[0].recipients, ["alexis@notmyidea.org"])
|
||||||
|
|
||||||
# sending a message to multiple persons
|
# sending a message to multiple persons
|
||||||
with web.mail.record_messages() as outbox:
|
with run.mail.record_messages() as outbox:
|
||||||
self.app.post("/raclette/invite", data=
|
self.app.post("/raclette/invite", data=
|
||||||
{"emails": 'alexis@notmyidea.org, toto@notmyidea.org'})
|
{"emails": 'alexis@notmyidea.org, toto@notmyidea.org'})
|
||||||
|
|
||||||
|
@ -80,13 +80,13 @@ class BudgetTestCase(TestCase):
|
||||||
["alexis@notmyidea.org", "toto@notmyidea.org"])
|
["alexis@notmyidea.org", "toto@notmyidea.org"])
|
||||||
|
|
||||||
# mail address checking
|
# mail address checking
|
||||||
with web.mail.record_messages() as outbox:
|
with run.mail.record_messages() as outbox:
|
||||||
response = self.app.post("/raclette/invite", data={"emails": "toto"})
|
response = self.app.post("/raclette/invite", data={"emails": "toto"})
|
||||||
self.assertEqual(len(outbox), 0) # no message sent
|
self.assertEqual(len(outbox), 0) # no message sent
|
||||||
self.assertIn("The email toto is not valid", response.data)
|
self.assertIn("The email toto is not valid", response.data)
|
||||||
|
|
||||||
# mixing good and wrong adresses shouldn't send any messages
|
# mixing good and wrong adresses shouldn't send any messages
|
||||||
with web.mail.record_messages() as outbox:
|
with run.mail.record_messages() as outbox:
|
||||||
self.app.post("/raclette/invite", data=
|
self.app.post("/raclette/invite", data=
|
||||||
{"emails": 'alexis@notmyidea.org, alexis'}) # not valid
|
{"emails": 'alexis@notmyidea.org, alexis'}) # not valid
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ class BudgetTestCase(TestCase):
|
||||||
|
|
||||||
|
|
||||||
def test_project_creation(self):
|
def test_project_creation(self):
|
||||||
with web.app.test_client() as c:
|
with run.app.test_client() as c:
|
||||||
|
|
||||||
# add a valid project
|
# add a valid project
|
||||||
c.post("/create", data={
|
c.post("/create", data={
|
||||||
|
@ -188,7 +188,7 @@ class BudgetTestCase(TestCase):
|
||||||
|
|
||||||
def test_demo(self):
|
def test_demo(self):
|
||||||
# Test that it is possible to connect automatically by going onto /demo
|
# Test that it is possible to connect automatically by going onto /demo
|
||||||
with web.app.test_client() as c:
|
with run.app.test_client() as c:
|
||||||
models.db.session.add(models.Project(id="demo", name=u"demonstration",
|
models.db.session.add(models.Project(id="demo", name=u"demonstration",
|
||||||
password="demo", contact_email="demo@notmyidea.org"))
|
password="demo", contact_email="demo@notmyidea.org"))
|
||||||
models.db.session.commit()
|
models.db.session.commit()
|
||||||
|
@ -213,7 +213,7 @@ class BudgetTestCase(TestCase):
|
||||||
self.assertIn("Authentication", resp.data)
|
self.assertIn("Authentication", resp.data)
|
||||||
|
|
||||||
# try to connect with wrong credentials should not work
|
# try to connect with wrong credentials should not work
|
||||||
with web.app.test_client() as c:
|
with run.app.test_client() as c:
|
||||||
resp = c.post("/authenticate",
|
resp = c.post("/authenticate",
|
||||||
data={'id': 'raclette', 'password': 'nope'})
|
data={'id': 'raclette', 'password': 'nope'})
|
||||||
|
|
||||||
|
@ -221,7 +221,7 @@ class BudgetTestCase(TestCase):
|
||||||
self.assertNotIn('raclette', session)
|
self.assertNotIn('raclette', session)
|
||||||
|
|
||||||
# try to connect with the right credentials should work
|
# try to connect with the right credentials should work
|
||||||
with web.app.test_client() as c:
|
with run.app.test_client() as c:
|
||||||
resp = c.post("/authenticate",
|
resp = c.post("/authenticate",
|
||||||
data={'id': 'raclette', 'password': 'raclette'})
|
data={'id': 'raclette', 'password': 'raclette'})
|
||||||
|
|
||||||
|
|
|
@ -9,21 +9,21 @@ from forms import (ProjectForm, AuthenticationForm, BillForm, MemberForm,
|
||||||
InviteForm, CreateArchiveForm)
|
InviteForm, CreateArchiveForm)
|
||||||
from utils import get_billform_for, Redirect303
|
from utils import get_billform_for, Redirect303
|
||||||
|
|
||||||
# create the application, initialize stuff
|
"""
|
||||||
app = Flask(__name__)
|
The blueprint for the web interface.
|
||||||
app.config.from_object("default_settings")
|
|
||||||
|
Contains all the interaction logic with the end user (except forms which
|
||||||
|
are directly handled in the forms module.
|
||||||
|
|
||||||
|
Basically, this blueprint takes care of the authentication and provides
|
||||||
|
some shortcuts to make your life better when coding (see `pull_project`
|
||||||
|
and `add_project_id` for a quick overview
|
||||||
|
"""
|
||||||
|
|
||||||
|
main = Blueprint("main", __name__)
|
||||||
mail = Mail()
|
mail = Mail()
|
||||||
|
|
||||||
# db
|
@main.url_defaults
|
||||||
db.init_app(app)
|
|
||||||
db.app = app
|
|
||||||
db.create_all()
|
|
||||||
|
|
||||||
# mail
|
|
||||||
mail.init_app(app)
|
|
||||||
|
|
||||||
|
|
||||||
@app.url_defaults
|
|
||||||
def add_project_id(endpoint, values):
|
def add_project_id(endpoint, values):
|
||||||
"""Add the project id to the url calls if it is expected.
|
"""Add the project id to the url calls if it is expected.
|
||||||
|
|
||||||
|
@ -31,10 +31,10 @@ def add_project_id(endpoint, values):
|
||||||
"""
|
"""
|
||||||
if 'project_id' in values or not hasattr(g, 'project'):
|
if 'project_id' in values or not hasattr(g, 'project'):
|
||||||
return
|
return
|
||||||
if app.url_map.is_endpoint_expecting(endpoint, 'project_id'):
|
if current_app.url_map.is_endpoint_expecting(endpoint, 'project_id'):
|
||||||
values['project_id'] = g.project.id
|
values['project_id'] = g.project.id
|
||||||
|
|
||||||
@app.url_value_preprocessor
|
@main.url_value_preprocessor
|
||||||
def pull_project(endpoint, values):
|
def pull_project(endpoint, values):
|
||||||
"""When a request contains a project_id value, transform it directly
|
"""When a request contains a project_id value, transform it directly
|
||||||
into a project by checking the credentials are stored in session.
|
into a project by checking the credentials are stored in session.
|
||||||
|
@ -49,16 +49,16 @@ def pull_project(endpoint, values):
|
||||||
if project_id:
|
if project_id:
|
||||||
project = Project.query.get(project_id)
|
project = Project.query.get(project_id)
|
||||||
if not project:
|
if not project:
|
||||||
raise Redirect303(url_for("create_project", project_id=project_id))
|
raise Redirect303(url_for(".create_project", project_id=project_id))
|
||||||
if project.id in session and session[project.id] == project.password:
|
if project.id in session and session[project.id] == project.password:
|
||||||
# add project into kwargs and call the original function
|
# add project into kwargs and call the original function
|
||||||
g.project = project
|
g.project = project
|
||||||
else:
|
else:
|
||||||
# redirect to authentication page
|
# redirect to authentication page
|
||||||
raise Redirect303(
|
raise Redirect303(
|
||||||
url_for("authenticate", project_id=project_id))
|
url_for(".authenticate", project_id=project_id))
|
||||||
|
|
||||||
@app.route("/authenticate", methods=["GET", "POST"])
|
@main.route("/authenticate", methods=["GET", "POST"])
|
||||||
def authenticate(project_id=None):
|
def authenticate(project_id=None):
|
||||||
"""Authentication form"""
|
"""Authentication form"""
|
||||||
form = AuthenticationForm()
|
form = AuthenticationForm()
|
||||||
|
@ -76,7 +76,7 @@ def authenticate(project_id=None):
|
||||||
# if credentials are already in session, redirect
|
# if credentials are already in session, redirect
|
||||||
if project_id in session and project.password == session[project_id]:
|
if project_id in session and project.password == session[project_id]:
|
||||||
setattr(g, 'project', project)
|
setattr(g, 'project', project)
|
||||||
return redirect(url_for("list_bills"))
|
return redirect(url_for(".list_bills"))
|
||||||
|
|
||||||
# else process the form
|
# else process the form
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
|
@ -92,19 +92,19 @@ def authenticate(project_id=None):
|
||||||
session[project_id] = form.password.data
|
session[project_id] = form.password.data
|
||||||
session.update()
|
session.update()
|
||||||
setattr(g, 'project', project)
|
setattr(g, 'project', project)
|
||||||
return redirect(url_for("list_bills"))
|
return redirect(url_for(".list_bills"))
|
||||||
|
|
||||||
return render_template("authenticate.html", form=form,
|
return render_template("authenticate.html", form=form,
|
||||||
create_project=create_project)
|
create_project=create_project)
|
||||||
|
|
||||||
@app.route("/")
|
@main.route("/")
|
||||||
def home():
|
def home():
|
||||||
project_form = ProjectForm()
|
project_form = ProjectForm()
|
||||||
auth_form = AuthenticationForm()
|
auth_form = AuthenticationForm()
|
||||||
return render_template("home.html", project_form=project_form,
|
return render_template("home.html", project_form=project_form,
|
||||||
auth_form=auth_form, session=session)
|
auth_form=auth_form, session=session)
|
||||||
|
|
||||||
@app.route("/create", methods=["GET", "POST"])
|
@main.route("/create", methods=["GET", "POST"])
|
||||||
def create_project():
|
def create_project():
|
||||||
form = ProjectForm()
|
form = ProjectForm()
|
||||||
if request.method == "GET" and 'project_id' in request.values:
|
if request.method == "GET" and 'project_id' in request.values:
|
||||||
|
@ -122,17 +122,17 @@ def create_project():
|
||||||
session.update()
|
session.update()
|
||||||
|
|
||||||
# redirect the user to the next step (invite)
|
# redirect the user to the next step (invite)
|
||||||
return redirect(url_for("invite", project_id=project.id))
|
return redirect(url_for(".invite", project_id=project.id))
|
||||||
|
|
||||||
return render_template("create_project.html", form=form)
|
return render_template("create_project.html", form=form)
|
||||||
|
|
||||||
@app.route("/exit")
|
@main.route("/exit")
|
||||||
def exit():
|
def exit():
|
||||||
# delete the session
|
# delete the session
|
||||||
session.clear()
|
session.clear()
|
||||||
return redirect(url_for("home"))
|
return redirect(url_for(".home"))
|
||||||
|
|
||||||
@app.route("/demo")
|
@main.route("/demo")
|
||||||
def demo():
|
def demo():
|
||||||
"""
|
"""
|
||||||
Authenticate the user for the demonstration project and redirect him to
|
Authenticate the user for the demonstration project and redirect him to
|
||||||
|
@ -147,9 +147,9 @@ def demo():
|
||||||
db.session.add(project)
|
db.session.add(project)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
session[project.id] = project.password
|
session[project.id] = project.password
|
||||||
return redirect(url_for("list_bills", project_id=project.id))
|
return redirect(url_for(".list_bills", project_id=project.id))
|
||||||
|
|
||||||
@app.route("/<project_id>/invite", methods=["GET", "POST"])
|
@main.route("/<project_id>/invite", methods=["GET", "POST"])
|
||||||
def invite():
|
def invite():
|
||||||
"""Send invitations for this particular project"""
|
"""Send invitations for this particular project"""
|
||||||
|
|
||||||
|
@ -169,11 +169,11 @@ def invite():
|
||||||
for email in form.emails.data.split(",")])
|
for email in form.emails.data.split(",")])
|
||||||
mail.send(msg)
|
mail.send(msg)
|
||||||
flash("You invitations have been sent")
|
flash("You invitations have been sent")
|
||||||
return redirect(url_for("list_bills"))
|
return redirect(url_for(".list_bills"))
|
||||||
|
|
||||||
return render_template("send_invites.html", form=form)
|
return render_template("send_invites.html", form=form)
|
||||||
|
|
||||||
@app.route("/<project_id>/")
|
@main.route("/<project_id>/")
|
||||||
def list_bills():
|
def list_bills():
|
||||||
bills = g.project.get_bills()
|
bills = g.project.get_bills()
|
||||||
return render_template("list_bills.html",
|
return render_template("list_bills.html",
|
||||||
|
@ -181,7 +181,7 @@ def list_bills():
|
||||||
bill_form=get_billform_for(g.project)
|
bill_form=get_billform_for(g.project)
|
||||||
)
|
)
|
||||||
|
|
||||||
@app.route("/<project_id>/members/add", methods=["GET", "POST"])
|
@main.route("/<project_id>/members/add", methods=["GET", "POST"])
|
||||||
def add_member():
|
def add_member():
|
||||||
# FIXME manage form errors on the list_bills page
|
# FIXME manage form errors on the list_bills page
|
||||||
form = MemberForm(g.project)
|
form = MemberForm(g.project)
|
||||||
|
@ -194,14 +194,14 @@ def add_member():
|
||||||
person[0].activated = True
|
person[0].activated = True
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash("%s is part of this project again" % person[0].name)
|
flash("%s is part of this project again" % person[0].name)
|
||||||
return redirect(url_for("list_bills"))
|
return redirect(url_for(".list_bills"))
|
||||||
|
|
||||||
db.session.add(Person(name=form.name.data, project=g.project))
|
db.session.add(Person(name=form.name.data, project=g.project))
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return redirect(url_for("list_bills"))
|
return redirect(url_for(".list_bills"))
|
||||||
return render_template("add_member.html", form=form)
|
return render_template("add_member.html", form=form)
|
||||||
|
|
||||||
@app.route("/<project_id>/members/<member_id>/reactivate", methods=["GET",])
|
@main.route("/<project_id>/members/<member_id>/reactivate", methods=["GET",])
|
||||||
def reactivate(member_id):
|
def reactivate(member_id):
|
||||||
person = Person.query.filter(Person.id == member_id)\
|
person = Person.query.filter(Person.id == member_id)\
|
||||||
.filter(Project.id == g.project.id).all()
|
.filter(Project.id == g.project.id).all()
|
||||||
|
@ -209,10 +209,10 @@ def reactivate(member_id):
|
||||||
person[0].activated = True
|
person[0].activated = True
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash("%s is part of this project again" % person[0].name)
|
flash("%s is part of this project again" % person[0].name)
|
||||||
return redirect(url_for("list_bills"))
|
return redirect(url_for(".list_bills"))
|
||||||
|
|
||||||
|
|
||||||
@app.route("/<project_id>/members/<member_id>/delete", methods=["GET", "POST"])
|
@main.route("/<project_id>/members/<member_id>/delete", methods=["GET", "POST"])
|
||||||
def remove_member(member_id):
|
def remove_member(member_id):
|
||||||
member = g.project.remove_member(member_id)
|
member = g.project.remove_member(member_id)
|
||||||
if member.activated == False:
|
if member.activated == False:
|
||||||
|
@ -220,9 +220,9 @@ def remove_member(member_id):
|
||||||
else:
|
else:
|
||||||
flash("User '%s' has been removed" % member.name)
|
flash("User '%s' has been removed" % member.name)
|
||||||
|
|
||||||
return redirect(url_for("list_bills"))
|
return redirect(url_for(".list_bills"))
|
||||||
|
|
||||||
@app.route("/<project_id>/add", methods=["GET", "POST"])
|
@main.route("/<project_id>/add", methods=["GET", "POST"])
|
||||||
def add_bill():
|
def add_bill():
|
||||||
form = get_billform_for(g.project)
|
form = get_billform_for(g.project)
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
|
@ -232,22 +232,22 @@ def add_bill():
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
flash("The bill has been added")
|
flash("The bill has been added")
|
||||||
return redirect(url_for('list_bills'))
|
return redirect(url_for('.list_bills'))
|
||||||
|
|
||||||
return render_template("add_bill.html", form=form)
|
return render_template("add_bill.html", form=form)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/<project_id>/delete/<int:bill_id>")
|
@main.route("/<project_id>/delete/<int:bill_id>")
|
||||||
def delete_bill(bill_id):
|
def delete_bill(bill_id):
|
||||||
bill = Bill.query.get_or_404(bill_id)
|
bill = Bill.query.get_or_404(bill_id)
|
||||||
db.session.delete(bill)
|
db.session.delete(bill)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash("The bill has been deleted")
|
flash("The bill has been deleted")
|
||||||
|
|
||||||
return redirect(url_for('list_bills'))
|
return redirect(url_for('.list_bills'))
|
||||||
|
|
||||||
|
|
||||||
@app.route("/<project_id>/edit/<int:bill_id>", methods=["GET", "POST"])
|
@main.route("/<project_id>/edit/<int:bill_id>", methods=["GET", "POST"])
|
||||||
def edit_bill(bill_id):
|
def edit_bill(bill_id):
|
||||||
bill = Bill.query.get_or_404(bill_id)
|
bill = Bill.query.get_or_404(bill_id)
|
||||||
form = get_billform_for(g.project, set_default=False)
|
form = get_billform_for(g.project, set_default=False)
|
||||||
|
@ -256,17 +256,17 @@ def edit_bill(bill_id):
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
flash("The bill has been modified")
|
flash("The bill has been modified")
|
||||||
return redirect(url_for('list_bills'))
|
return redirect(url_for('.list_bills'))
|
||||||
|
|
||||||
form.fill(bill)
|
form.fill(bill)
|
||||||
return render_template("add_bill.html", form=form, edit=True)
|
return render_template("add_bill.html", form=form, edit=True)
|
||||||
|
|
||||||
@app.route("/<project_id>/compute")
|
@main.route("/<project_id>/compute")
|
||||||
def compute_bills():
|
def compute_bills():
|
||||||
"""Compute the sum each one have to pay to each other and display it"""
|
"""Compute the sum each one have to pay to each other and display it"""
|
||||||
return render_template("compute_bills.html")
|
return render_template("compute_bills.html")
|
||||||
|
|
||||||
@app.route("/<project_id>/archives/create")
|
@main.route("/<project_id>/archives/create")
|
||||||
def create_archive():
|
def create_archive():
|
||||||
form = CreateArchiveForm()
|
form = CreateArchiveForm()
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
|
@ -275,10 +275,3 @@ def create_archive():
|
||||||
flash("The data from XX to XX has been archived")
|
flash("The data from XX to XX has been archived")
|
||||||
|
|
||||||
return render_template("create_archive.html", form=form)
|
return render_template("create_archive.html", form=form)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
app.run(host="0.0.0.0", debug=True)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[program:budget]
|
[program:budget]
|
||||||
command=/path/to/your/app/venv/bin/gunicorn -c /path/to/your/app/conf/gunicorn.conf.py budget:app
|
command=/path/to/your/app/venv/bin/gunicorn -c /path/to/your/app/conf/gunicorn.conf.py run:app
|
||||||
directory=/path/to/your/app/budget/
|
directory=/path/to/your/app/budget/
|
||||||
user=www
|
user=www
|
||||||
autostart=true
|
autostart=true
|
||||||
|
|
Loading…
Reference in a new issue