From 830718e1fe5f18959f455a696ebc2172a2d5f253 Mon Sep 17 00:00:00 2001 From: Richard Coates Date: Thu, 25 Jan 2018 17:34:37 +0100 Subject: [PATCH 01/21] Make sidebar scrollable (#316) * Make sidebar scrollable Make sidebar scrollable. * Update CHANGELOG.rst Fixes #318 --- CHANGELOG.rst | 1 + ihatemoney/static/css/main.css | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9aa61bef..efd0648b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -10,6 +10,7 @@ Fixed ===== - Fix the "IOError" crash when running `ihatemoney generate-config` (#308) +- Made the left-hand sidebar scrollable (#318) 2.0 (2017-12-27) diff --git a/ihatemoney/static/css/main.css b/ihatemoney/static/css/main.css index b0120ca9..4b35b376 100644 --- a/ihatemoney/static/css/main.css +++ b/ihatemoney/static/css/main.css @@ -74,6 +74,7 @@ body { background-repeat: no-repeat; height: 100%; color: black; + overflow-y: scroll; } @media (min-width: 768px) { From b93ea4830d5290def99d597f17292a8aa5d4c090 Mon Sep 17 00:00:00 2001 From: 0livd Date: Thu, 25 Jan 2018 17:41:28 +0100 Subject: [PATCH 02/21] API: Migrate from flask-rest to flask-restful (#315) The flask-rest custom json encoder is still needed and thus was added to ihatemoney's utils. Closes #298 --- CHANGELOG.rst | 5 ++ ihatemoney/api.py | 158 +++++++++++++++++++------------------- ihatemoney/run.py | 4 +- ihatemoney/tests/tests.py | 14 ++-- ihatemoney/utils.py | 27 ++++++- requirements.txt | 2 +- 6 files changed, 121 insertions(+), 89 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index efd0648b..ea2b61e5 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,11 @@ This document describes changes between each past release. 2.1 (unreleased) ---------------- +Changed +======= + +- Use flask-restful instead of deprecated flask-rest for the REST API (#315) + Fixed ===== diff --git a/ihatemoney/api.py b/ihatemoney/api.py index 827202c8..31ed06cc 100644 --- a/ihatemoney/api.py +++ b/ihatemoney/api.py @@ -1,62 +1,68 @@ # -*- coding: utf-8 -*- from flask import Blueprint, request -from flask_rest import RESTResource, need_auth +from flask_restful import Resource, Api, abort from wtforms.fields.core import BooleanField from ihatemoney.models import db, Project, Person, Bill from ihatemoney.forms import (ProjectForm, EditProjectForm, MemberForm, get_billform_for) from werkzeug.security import check_password_hash +from functools import wraps api = Blueprint("api", __name__, url_prefix="/api") +restful_api = Api(api) -def check_project(*args, **kwargs): +def need_auth(f): """Check the request for basic authentication for a given project. - Return the project if the authorization is good, False otherwise + Return the project if the authorization is good, abort the request with a 401 otherwise """ - auth = request.authorization + @wraps(f) + def wrapper(*args, **kwargs): + auth = request.authorization + project_id = kwargs.get("project_id") - # project_id should be contained in kwargs and equal to the username - if auth and "project_id" in kwargs and \ - auth.username == kwargs["project_id"]: - project = Project.query.get(auth.username) - if project and check_password_hash(project.password, auth.password): - return project - return False + if auth and project_id and auth.username == project_id: + project = Project.query.get(auth.username) + if project and check_password_hash(project.password, auth.password): + # The whole project object will be passed instead of project_id + kwargs.pop("project_id") + return f(*args, project=project, **kwargs) + abort(401) + return wrapper -class ProjectHandler(object): - - def add(self): +class ProjectsHandler(Resource): + def post(self): form = ProjectForm(meta={'csrf': False}) if form.validate(): project = form.save() db.session.add(project) db.session.commit() - return 201, project.id - return 400, form.errors + return project.id, 201 + return form.errors, 400 + + +class ProjectHandler(Resource): + method_decorators = [need_auth] - @need_auth(check_project, "project") def get(self, project): - return 200, project + return project - @need_auth(check_project, "project") def delete(self, project): db.session.delete(project) db.session.commit() - return 200, "DELETED" + return "DELETED" - @need_auth(check_project, "project") - def update(self, project): + def put(self, project): form = EditProjectForm(meta={'csrf': False}) if form.validate(): form.update(project) db.session.commit() - return 200, "UPDATED" - return 400, form.errors + return "UPDATED" + return form.errors, 400 class APIMemberForm(MemberForm): @@ -71,98 +77,92 @@ class APIMemberForm(MemberForm): return super(APIMemberForm, self).save(project, person) -class MemberHandler(object): +class MembersHandler(Resource): + method_decorators = [need_auth] - def get(self, project, member_id): - member = Person.query.get(member_id, project) - if not member or member.project != project: - return 404, "Not Found" - return 200, member + def get(self, project): + return project.members - def list(self, project): - return 200, project.members - - def add(self, project): + def post(self, project): form = MemberForm(project, meta={'csrf': False}) if form.validate(): member = Person() form.save(project, member) db.session.commit() - return 201, member.id - return 400, form.errors + return member.id, 201 + return form.errors, 400 - def update(self, project, member_id): + +class MemberHandler(Resource): + method_decorators = [need_auth] + + def get(self, project, member_id): + member = Person.query.get(member_id, project) + if not member or member.project != project: + return "Not Found", 404 + return member + + def put(self, project, member_id): form = APIMemberForm(project, meta={'csrf': False}, edit=True) if form.validate(): member = Person.query.get(member_id, project) form.save(project, member) db.session.commit() - return 200, member - return 400, form.errors + return member + return form.errors, 400 def delete(self, project, member_id): if project.remove_member(member_id): - return 200, "OK" - return 404, "Not Found" + return "OK" + return "Not Found", 404 -class BillHandler(object): +class BillsHandler(Resource): + method_decorators = [need_auth] - def get(self, project, bill_id): - bill = Bill.query.get(project, bill_id) - if not bill: - return 404, "Not Found" - return 200, bill - - def list(self, project): + def get(self, project): return project.get_bills().all() - def add(self, project): + def post(self, project): form = get_billform_for(project, True, meta={'csrf': False}) if form.validate(): bill = Bill() form.save(bill, project) db.session.add(bill) db.session.commit() - return 201, bill.id - return 400, form.errors + return bill.id, 201 + return form.errors, 400 - def update(self, project, bill_id): + +class BillHandler(Resource): + method_decorators = [need_auth] + + def get(self, project, bill_id): + bill = Bill.query.get(project, bill_id) + if not bill: + return "Not Found", 404 + return bill, 200 + + def put(self, project, bill_id): form = get_billform_for(project, True, meta={'csrf': False}) if form.validate(): bill = Bill.query.get(project, bill_id) form.save(bill, project) db.session.commit() - return 200, bill.id - return 400, form.errors + return bill.id, 200 + return form.errors, 400 def delete(self, project, bill_id): bill = Bill.query.delete(project, bill_id) db.session.commit() if not bill: - return 404, "Not Found" - return 200, "OK" + return "Not Found", 404 + return "OK", 200 -project_resource = RESTResource( - name="project", - route="/projects", - app=api, - actions=["add", "update", "delete", "get"], - handler=ProjectHandler()) - -member_resource = RESTResource( - name="member", - inject_name="project", - route="/projects//members", - app=api, - handler=MemberHandler(), - authentifier=check_project) - -bill_resource = RESTResource( - name="bill", - inject_name="project", - route="/projects//bills", - app=api, - handler=BillHandler(), - authentifier=check_project) +restful_api.add_resource(ProjectsHandler, '/projects') +restful_api.add_resource(ProjectHandler, '/projects/') +restful_api.add_resource(MembersHandler, "/projects//members") +restful_api.add_resource(MemberHandler, "/projects//members/") +restful_api.add_resource(BillsHandler, "/projects//bills") +restful_api.add_resource(BillHandler, "/projects//bills/") diff --git a/ihatemoney/run.py b/ihatemoney/run.py index e3a7c1e5..a8de26f0 100644 --- a/ihatemoney/run.py +++ b/ihatemoney/run.py @@ -11,7 +11,7 @@ from werkzeug.contrib.fixers import ProxyFix from ihatemoney.api import api from ihatemoney.models import db -from ihatemoney.utils import PrefixedWSGI, minimal_round +from ihatemoney.utils import PrefixedWSGI, minimal_round, IhmJSONEncoder from ihatemoney.web import main as web_interface from ihatemoney import default_settings @@ -68,6 +68,8 @@ def load_configuration(app, configuration=None): app.config.from_pyfile(env_var_config) else: app.config.from_pyfile('ihatemoney.cfg', silent=True) + # Configure custom JSONEncoder used by the API + app.config['RESTFUL_JSON'] = {'cls': IhmJSONEncoder} def validate_configuration(app): diff --git a/ihatemoney/tests/tests.py b/ihatemoney/tests/tests.py index d4b6d7a1..c13131c4 100644 --- a/ihatemoney/tests/tests.py +++ b/ihatemoney/tests/tests.py @@ -1053,7 +1053,7 @@ class APITestCase(IhatemoneyTestCase): }) self.assertTrue(400, resp.status_code) - self.assertEqual('{"contact_email": ["Invalid email address."]}', + self.assertEqual('{"contact_email": ["Invalid email address."]}\n', resp.data.decode('utf-8')) # create it @@ -1139,7 +1139,7 @@ class APITestCase(IhatemoneyTestCase): headers=self.get_auth("raclette")) self.assertStatus(200, req) - self.assertEqual('[]', req.data.decode('utf-8')) + self.assertEqual('[]\n', req.data.decode('utf-8')) # add a member req = self.client.post("/api/projects/raclette/members", data={ @@ -1148,7 +1148,7 @@ class APITestCase(IhatemoneyTestCase): # the id of the new member should be returned self.assertStatus(201, req) - self.assertEqual("1", req.data.decode('utf-8')) + self.assertEqual("1\n", req.data.decode('utf-8')) # the list of members should contain one member req = self.client.get("/api/projects/raclette/members", @@ -1223,7 +1223,7 @@ class APITestCase(IhatemoneyTestCase): headers=self.get_auth("raclette")) self.assertStatus(200, req) - self.assertEqual('[]', req.data.decode('utf-8')) + self.assertEqual('[]\n', req.data.decode('utf-8')) def test_bills(self): # create a project @@ -1239,7 +1239,7 @@ class APITestCase(IhatemoneyTestCase): headers=self.get_auth("raclette")) self.assertStatus(200, req) - self.assertEqual("[]", req.data.decode('utf-8')) + self.assertEqual("[]\n", req.data.decode('utf-8')) # add a bill req = self.client.post("/api/projects/raclette/bills", data={ @@ -1252,7 +1252,7 @@ class APITestCase(IhatemoneyTestCase): # should return the id self.assertStatus(201, req) - self.assertEqual(req.data.decode('utf-8'), "1") + self.assertEqual(req.data.decode('utf-8'), "1\n") # get this bill details req = self.client.get("/api/projects/raclette/bills/1", @@ -1288,7 +1288,7 @@ class APITestCase(IhatemoneyTestCase): }, headers=self.get_auth("raclette")) self.assertStatus(400, req) - self.assertEqual('{"date": ["This field is required."]}', req.data.decode('utf-8')) + self.assertEqual('{"date": ["This field is required."]}\n', req.data.decode('utf-8')) # edit a bill req = self.client.put("/api/projects/raclette/bills/1", data={ diff --git a/ihatemoney/utils.py b/ihatemoney/utils.py index 6af0112c..aaae2a08 100644 --- a/ihatemoney/utils.py +++ b/ihatemoney/utils.py @@ -3,7 +3,7 @@ import re from io import BytesIO, StringIO from jinja2 import filters -from json import dumps +from json import dumps, JSONEncoder from flask import redirect from werkzeug.routing import HTTPException, RoutingException import six @@ -170,3 +170,28 @@ class LoginThrottler(): def reset(self, ip): self._attempts.pop(ip, None) + + +class IhmJSONEncoder(JSONEncoder): + """Subclass of the default encoder to support custom objects. + Taken from the deprecated flask-rest package.""" + def default(self, o): + if hasattr(o, "_to_serialize"): + # build up the object + data = {} + for attr in o._to_serialize: + data[attr] = getattr(o, attr) + return data + elif hasattr(o, "isoformat"): + return o.isoformat() + else: + try: + from flask_babel import speaklater + if isinstance(o, speaklater.LazyString): + try: + return unicode(o) # For python 2. + except NameError: + return str(o) # For python 3. + except ImportError: + pass + return JSONEncoder.default(self, o) diff --git a/requirements.txt b/requirements.txt index 64610abd..c2fe5348 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ flask-mail>=0.8 Flask-Migrate>=1.8.0 Flask-script flask-babel -flask-rest>=1.3 +flask-restful>=0.3.6 jinja2>=2.6 raven blinker From 434ee8b85251ee14cb0535cbb92b9b28b84f0b8d Mon Sep 17 00:00:00 2001 From: Jocelyn Delalande Date: Fri, 26 Jan 2018 16:17:43 +0100 Subject: [PATCH 03/21] Display sidebar scroll-bar only if required This fix a regression from #316 (scrollbar was displayed all the time). Note that the padding-bottom value is totally empiric, but proved OK on my Fx and Chrome instances + some responsive tests. There might be finer solutions, feel free :-). --- ihatemoney/static/css/main.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ihatemoney/static/css/main.css b/ihatemoney/static/css/main.css index 4b35b376..94ca4bd8 100644 --- a/ihatemoney/static/css/main.css +++ b/ihatemoney/static/css/main.css @@ -74,12 +74,13 @@ body { background-repeat: no-repeat; height: 100%; color: black; - overflow-y: scroll; + overflow-y: auto; } @media (min-width: 768px) { .sidebar { position: fixed; + padding-bottom: 4.5rem; } } From cf7bd572489aad17bf60026c6618d0ff49f822a9 Mon Sep 17 00:00:00 2001 From: Jocelyn Delalande Date: Sun, 4 Feb 2018 13:10:57 +0100 Subject: [PATCH 04/21] Remove unused CSS odd/even classes That was forgotten from fe39258630e55d4a3e1297a01a1c8fd39bad3a4e --- ihatemoney/templates/dashboard.html | 2 +- ihatemoney/templates/settle_bills.html | 2 +- ihatemoney/templates/statistics.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ihatemoney/templates/dashboard.html b/ihatemoney/templates/dashboard.html index b1220bd4..807e3e2f 100644 --- a/ihatemoney/templates/dashboard.html +++ b/ihatemoney/templates/dashboard.html @@ -4,7 +4,7 @@ {% for project in projects|sort(attribute='name') %} - + {% if project.has_bills() %} diff --git a/ihatemoney/templates/settle_bills.html b/ihatemoney/templates/settle_bills.html index b67a9b85..7ec5e290 100644 --- a/ihatemoney/templates/settle_bills.html +++ b/ihatemoney/templates/settle_bills.html @@ -22,7 +22,7 @@ {% for bill in bills %} - + diff --git a/ihatemoney/templates/statistics.html b/ihatemoney/templates/statistics.html index 061c6299..ae1c80ea 100644 --- a/ihatemoney/templates/statistics.html +++ b/ihatemoney/templates/statistics.html @@ -22,7 +22,7 @@ {% for member in members %} - + From 389c7b8bcd2813d8549858265432859259942fd6 Mon Sep 17 00:00:00 2001 From: Jocelyn Delalande Date: Sat, 3 Feb 2018 17:49:12 +0100 Subject: [PATCH 05/21] Remove dead code --- ihatemoney/templates/statistics.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ihatemoney/templates/statistics.html b/ihatemoney/templates/statistics.html index ae1c80ea..ee59cb9f 100644 --- a/ihatemoney/templates/statistics.html +++ b/ihatemoney/templates/statistics.html @@ -5,7 +5,7 @@
{{ _("Project") }}{{ _("Number of members") }}{{ _("Number of bills") }}{{_("Newest bill")}}{{_("Oldest bill")}}{{_("Actions")}}
{{ project.name }}{{ project.members | count }}{{ project.get_bills().count() }}{{ project.get_bills().all()[0].date }}
{{ _("Who pays?") }}{{ _("To whom?") }}{{ _("How much?") }}
{{ bill.ower }} {{ bill.receiver }} {{ "%0.2f"|format(bill.amount) }}
{{ _("Who?") }}{{ _("Paid") }}{{ _("Spent") }}{{ _("Balance") }}
{{ member.name }} {{ "%0.2f"|format(paid[member.id]) }} {{ "%0.2f"|format(spent[member.id]) }}
{% set balance = g.project.balance %} {% for member in g.project.members | sort(attribute='name') if member.activated or balance[member.id]|round(2) != 0 %} - +
{{ member.name }} {% if balance[member.id]|round(2) > 0 %}+{% endif %}{{ "%.2f" | format(balance[member.id]) }} From b1a4572e8c72e1d7f49b07aaeb5be0f3603bf0a7 Mon Sep 17 00:00:00 2001 From: Jocelyn Delalande Date: Sat, 3 Feb 2018 18:04:06 +0100 Subject: [PATCH 06/21] Change statistics data structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clearer data structure, and simpler template This commit has a side effect: sidebar now hides disabled members. IMHO, the disabled members should either be hidden or shown consistently between sidebar and central table. Previous status was: shown in sidebar (if balance ≠ 0) and hidden in central table. --- ihatemoney/templates/statistics.html | 19 +++++++++---------- ihatemoney/tests/tests.py | 24 ++++++++++++------------ ihatemoney/web.py | 27 ++++++++++++++------------- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/ihatemoney/templates/statistics.html b/ihatemoney/templates/statistics.html index ee59cb9f..1b07a33f 100644 --- a/ihatemoney/templates/statistics.html +++ b/ihatemoney/templates/statistics.html @@ -3,12 +3,11 @@ {% block sidebar %}
- {% set balance = g.project.balance %} - {% for member in g.project.members | sort(attribute='name') if member.activated or balance[member.id]|round(2) != 0 %} + {% for stat in members_stats| sort(attribute='member.name') %} - - + {% endfor %} @@ -21,12 +20,12 @@
{{ member.name }} - {% if balance[member.id]|round(2) > 0 %}+{% endif %}{{ "%.2f" | format(balance[member.id]) }} + {{ stat.member.name }} + {% if stat.balance|round(2) > 0 %}+{% endif %}{{ "%.2f" | format(stat.balance) }}
- {% for member in members %} + {% for stat in members_stats %} - - - - + + + + {% endfor %} diff --git a/ihatemoney/tests/tests.py b/ihatemoney/tests/tests.py index c13131c4..35820325 100644 --- a/ihatemoney/tests/tests.py +++ b/ihatemoney/tests/tests.py @@ -750,24 +750,24 @@ class BudgetTestCase(IhatemoneyTestCase): }) response = self.client.get("/raclette/statistics") - self.assertIn("\n " - + "\n " - + "\n " + self.assertIn("\n " + + "\n " + + "\n " + "\n", response.data.decode('utf-8')) - self.assertIn("\n " - + "\n " - + "\n " + self.assertIn("\n " + + "\n " + + "\n " + "\n", response.data.decode('utf-8')) - self.assertIn("\n " - + "\n " - + "\n " + self.assertIn("\n " + + "\n " + + "\n " + "\n", response.data.decode('utf-8')) - self.assertIn("\n " - + "\n " - + "\n " + self.assertIn("\n " + + "\n " + + "\n " + "\n", response.data.decode('utf-8')) diff --git a/ihatemoney/web.py b/ihatemoney/web.py index 6b1b3589..85b02e5b 100644 --- a/ihatemoney/web.py +++ b/ihatemoney/web.py @@ -566,21 +566,22 @@ def settle_bill(): @main.route("//statistics") def statistics(): """Compute what each member has paid and spent and display it""" - members = g.project.active_members - balance = g.project.balance - paid = {} - spent = {} - for member in members: - paid[member.id] = sum([bill.amount - for bill in g.project.get_member_bills(member.id).all()]) - spent[member.id] = sum([bill.pay_each() * member.weight - for bill in g.project.get_bills().all() if member in bill.owers]) + members_stats = [{ + 'member': member, + 'paid': sum([ + bill.amount + for bill in g.project.get_member_bills(member.id).all() + ]), + 'spent': sum([ + bill.pay_each() * member.weight + for bill in g.project.get_bills().all() if member in bill.owers + ]), + 'balance': g.project.balance[member.id] + } for member in g.project.active_members] + return render_template( "statistics.html", - members=members, - balance=balance, - paid=paid, - spent=spent, + members_stats=members_stats, current_view='statistics', ) From 036cd05e5716a694f575b3c65f6541f04a8b48bf Mon Sep 17 00:00:00 2001 From: Jocelyn Delalande Date: Sat, 3 Feb 2018 18:26:44 +0100 Subject: [PATCH 07/21] Move member stats computation to a dedicated method --- ihatemoney/models.py | 20 ++++++++++++++++++++ ihatemoney/web.py | 15 +-------------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/ihatemoney/models.py b/ihatemoney/models.py index aa3083d6..c6ce23fb 100644 --- a/ihatemoney/models.py +++ b/ihatemoney/models.py @@ -52,6 +52,26 @@ class Project(db.Model): return balances + @property + def members_stats(self): + """Compute what each member has paid + + :return: one stat dict per member + :rtype list: + """ + return [{ + 'member': member, + 'paid': sum([ + bill.amount + for bill in self.get_member_bills(member.id).all() + ]), + 'spent': sum([ + bill.pay_each() * member.weight + for bill in self.get_bills().all() if member in bill.owers + ]), + 'balance': self.balance[member.id] + } for member in self.active_members] + @property def uses_weights(self): return len([i for i in self.members if i.weight != 1]) > 0 diff --git a/ihatemoney/web.py b/ihatemoney/web.py index 85b02e5b..1e162024 100644 --- a/ihatemoney/web.py +++ b/ihatemoney/web.py @@ -566,22 +566,9 @@ def settle_bill(): @main.route("//statistics") def statistics(): """Compute what each member has paid and spent and display it""" - members_stats = [{ - 'member': member, - 'paid': sum([ - bill.amount - for bill in g.project.get_member_bills(member.id).all() - ]), - 'spent': sum([ - bill.pay_each() * member.weight - for bill in g.project.get_bills().all() if member in bill.owers - ]), - 'balance': g.project.balance[member.id] - } for member in g.project.active_members] - return render_template( "statistics.html", - members_stats=members_stats, + members_stats=g.project.members_stats, current_view='statistics', ) From b95ea7f4e68a0794a44e68621a8210bb4db43e67 Mon Sep 17 00:00:00 2001 From: Jocelyn Delalande Date: Sat, 3 Feb 2018 18:52:04 +0100 Subject: [PATCH 08/21] Add statistics support to API --- CHANGELOG.rst | 5 +++++ docs/api.rst | 22 ++++++++++++++++++++++ ihatemoney/api.py | 8 ++++++++ ihatemoney/tests/tests.py | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ea2b61e5..fccb08d0 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -17,6 +17,11 @@ Fixed - Fix the "IOError" crash when running `ihatemoney generate-config` (#308) - Made the left-hand sidebar scrollable (#318) +Added +===== + +- Statistics API (#343) + 2.0 (2017-12-27) ---------------- diff --git a/docs/api.rst b/docs/api.rst index b82c6f3e..0ae42144 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -164,3 +164,25 @@ And you can of course `DELETE` them at `/api/projects//bills/`:: $ curl --basic -u demo:demo -X DELETE\ https://ihatemoney.org/api/projects/demo/bills/80\ "OK" + + +Statistics +---------- + +You can get some project stats with a `GET` on `/api/projects//statistics`:: + + $ curl --basic -u demo:demo https://ihatemoney.org/api/projects/demo/statistics + [ + { + "balance": 12.5, + "member": {"activated": True, "id": 1, "name": "alexis", "weight": 1.0}, + "paid": 25.0, + "spent": 12.5 + }, + { + "balance": -12.5, + "member": {"activated": True, "id": 2, "name": "fred", "weight": 1.0}, + "paid": 0, + "spent": 12.5 + } + ] diff --git a/ihatemoney/api.py b/ihatemoney/api.py index 31ed06cc..6068cf72 100644 --- a/ihatemoney/api.py +++ b/ihatemoney/api.py @@ -65,6 +65,13 @@ class ProjectHandler(Resource): return form.errors, 400 +class ProjectStatsHandler(Resource): + method_decorators = [need_auth] + + def get(self, project): + return project.members_stats + + class APIMemberForm(MemberForm): """ Member is not disablable via a Form. @@ -163,6 +170,7 @@ class BillHandler(Resource): restful_api.add_resource(ProjectsHandler, '/projects') restful_api.add_resource(ProjectHandler, '/projects/') restful_api.add_resource(MembersHandler, "/projects//members") +restful_api.add_resource(ProjectStatsHandler, "/projects//statistics") restful_api.add_resource(MemberHandler, "/projects//members/") restful_api.add_resource(BillsHandler, "/projects//bills") restful_api.add_resource(BillHandler, "/projects//bills/") diff --git a/ihatemoney/tests/tests.py b/ihatemoney/tests/tests.py index 35820325..3797f09d 100644 --- a/ihatemoney/tests/tests.py +++ b/ihatemoney/tests/tests.py @@ -1325,6 +1325,40 @@ class APITestCase(IhatemoneyTestCase): headers=self.get_auth("raclette")) self.assertStatus(404, req) + def test_statistics(self): + # create a project + self.api_create("raclette") + + # add members + self.api_add_member("raclette", "alexis") + self.api_add_member("raclette", "fred") + + # add a bill + req = self.client.post("/api/projects/raclette/bills", data={ + 'date': '2011-08-10', + 'what': 'fromage', + 'payer': "1", + 'payed_for': ["1", "2"], + 'amount': '25', + }, headers=self.get_auth("raclette")) + + # get the list of bills (should be empty) + req = self.client.get("/api/projects/raclette/statistics", + headers=self.get_auth("raclette")) + self.assertStatus(200, req) + self.assertEqual([ + {'balance': 12.5, + 'member': {'activated': True, 'id': 1, + 'name': 'alexis', 'weight': 1.0}, + 'paid': 25.0, + 'spent': 12.5}, + {'balance': -12.5, + 'member': {'activated': True, 'id': 2, + 'name': 'fred', 'weight': 1.0}, + 'paid': 0, + 'spent': 12.5}], + json.loads(req.data.decode('utf-8'))) + def test_username_xss(self): # create a project # self.api_create("raclette") From d9471733f82f948e4e77ac91d6280fa5c4856358 Mon Sep 17 00:00:00 2001 From: 0livd Date: Wed, 7 Feb 2018 09:37:21 +0100 Subject: [PATCH 09/21] Fix some anti patterns in docker deployment (#321) - Use exec to run gunicorn and avoid creating a new process. - Add the possibility to pass any additional parameters to gunicorn. - Use only one gunicorn worker by default as the usual way to scale the app in production would be to use the scale command of the cluster scheduler. Additional workers could still be added by passing the "-w" gunicorn parameter to docker run. --- Dockerfile | 5 ++--- conf/confandrun.sh | 9 +++++---- docs/installation.rst | 5 +++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index ba2752a1..feb423da 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,9 +26,8 @@ ENV DEBUG="False" \ ACTIVATE_DEMO_PROJECT="True" \ ADMIN_PASSWORD="" \ ALLOW_PUBLIC_PROJECT_CREATION="True" \ - ACTIVATE_ADMIN_DASHBOARD="False" \ - GUNICORN_NUM_WORKERS="3" + ACTIVATE_ADMIN_DASHBOARD="False" VOLUME /database EXPOSE 8000 -CMD ["/ihatemoney/conf/confandrun.sh"] +ENTRYPOINT ["/ihatemoney/conf/confandrun.sh"] diff --git a/conf/confandrun.sh b/conf/confandrun.sh index e76a8e8a..e37a5739 100755 --- a/conf/confandrun.sh +++ b/conf/confandrun.sh @@ -17,7 +17,8 @@ ADMIN_PASSWORD = "$ADMIN_PASSWORD" ALLOW_PUBLIC_PROJECT_CREATION = $ALLOW_PUBLIC_PROJECT_CREATION ACTIVATE_ADMIN_DASHBOARD = $ACTIVATE_ADMIN_DASHBOARD EOF -gunicorn ihatemoney.wsgi:application \ --b 0.0.0.0:8000 \ ---log-syslog \ --w "$GUNICORN_NUM_WORKERS" +# Start gunicorn without forking +exec gunicorn ihatemoney.wsgi:application \ + -b 0.0.0.0:8000 \ + --log-syslog \ + "$@" diff --git a/docs/installation.rst b/docs/installation.rst index 0c3cfac1..e7d586ec 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -154,9 +154,10 @@ A volume can also be specified to persist the default database file:: docker run -d -p 8000:8000 -v /host/path/to/database:/database ihatemoney -The following gunicorn parameters are also available:: +Additional gunicorn parameters can be passed using the docker ``CMD`` parameter. +For example, use the following command to add more gunicorn workers:: - GUNICORN_NUM_WORKERS (default: 3) + docker run -d -p 8000:8000 ihatemoney -w 3 Configuration ============= From 225849ac716aee7627961b92f9d627e7bfde4430 Mon Sep 17 00:00:00 2001 From: 0livd Date: Wed, 7 Feb 2018 09:37:49 +0100 Subject: [PATCH 10/21] Docker: Fix gunicorn not using ihm system package (#320) Fixes #319 --- Dockerfile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index feb423da..fbc0f4ac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,12 +4,11 @@ RUN mkdir /ihatemoney &&\ mkdir -p /etc/ihatemoney &&\ pip install --no-cache-dir gunicorn pymysql -WORKDIR /ihatemoney -COPY . . +COPY . /ihatemoney ARG INSTALL_FROM_PYPI="False" RUN if [ "$INSTALL_FROM_PYPI" = True ]; then\ pip install --no-cache-dir ihatemoney ; else\ - pip install --no-cache-dir -e . ; \ + pip install --no-cache-dir -e /ihatemoney ; \ fi ENV DEBUG="False" \ From 131fe8a8b4c6aeafc35f455b60f208d6e0268812 Mon Sep 17 00:00:00 2001 From: Jocelyn Delalande Date: Fri, 16 Feb 2018 23:13:51 +0100 Subject: [PATCH 11/21] Add missing CHANGELOG and CONTRIBUTORS entries for v2.1 --- CHANGELOG.rst | 6 +++++- CONTRIBUTORS | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index fccb08d0..95c7a311 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,24 +3,28 @@ Changelog This document describes changes between each past release. -2.1 (unreleased) +2.1 (2018-02-16) ---------------- Changed ======= - Use flask-restful instead of deprecated flask-rest for the REST API (#315) +- Make sidebar scrollable. Usefull for large groups (#316) Fixed ===== - Fix the "IOError" crash when running `ihatemoney generate-config` (#308) - Made the left-hand sidebar scrollable (#318) +- Fix and enhanche Docker support (#320, #321) Added ===== - Statistics API (#343) +- Allow to disable/enable member via API (#301) +- Enable basic Apache auth passthrough for API (#303) 2.0 (2017-12-27) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 9a817980..008ae90c 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -9,6 +9,7 @@ Alexis Metaireau Arnaud Bos Baptiste Jonglez Berteh +donkers Feth AREZKI Frédéric Sureau Jocelyn Delalande From d3d1ddfa9164a43695fc921d22c348e25256db66 Mon Sep 17 00:00:00 2001 From: Jocelyn Delalande Date: Fri, 16 Feb 2018 23:18:12 +0100 Subject: [PATCH 12/21] Preparing release 2.1 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 6531286f..b623978b 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ ENTRY_POINTS = { setup(name='ihatemoney', - version='2.1.dev0', + version='2.1', description='A simple shared budget manager web application.', long_description="{}\n\n{}".format(README.encode('utf-8'), CHANGELOG.encode('utf-8')), license='Custom BSD Beerware', From c3b2af3df30f12964dbebad105e05af43de33af9 Mon Sep 17 00:00:00 2001 From: Jocelyn Delalande Date: Fri, 16 Feb 2018 23:21:36 +0100 Subject: [PATCH 13/21] Back to development: 2.1.1 --- CHANGELOG.rst | 6 ++++++ setup.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 95c7a311..81df6bbf 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,6 +3,12 @@ Changelog This document describes changes between each past release. +2.1.1 (unreleased) +------------------ + +- Nothing changed yet. + + 2.1 (2018-02-16) ---------------- diff --git a/setup.py b/setup.py index b623978b..ec40c852 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ ENTRY_POINTS = { setup(name='ihatemoney', - version='2.1', + version='2.1.1.dev0', description='A simple shared budget manager web application.', long_description="{}\n\n{}".format(README.encode('utf-8'), CHANGELOG.encode('utf-8')), license='Custom BSD Beerware', From 74f72a46d55fbf1507acbe5ad9ac3df9e92a822e Mon Sep 17 00:00:00 2001 From: Adrien CLERC Date: Tue, 15 May 2018 21:45:24 +0200 Subject: [PATCH 14/21] new extraction with pybabel --- ihatemoney/messages.pot | 388 +++++++++++++++++++++++++--------------- 1 file changed, 245 insertions(+), 143 deletions(-) diff --git a/ihatemoney/messages.pot b/ihatemoney/messages.pot index 0b1759b3..4a152593 100644 --- a/ihatemoney/messages.pot +++ b/ihatemoney/messages.pot @@ -1,111 +1,127 @@ # Translations template for PROJECT. -# Copyright (C) 2013 ORGANIZATION +# Copyright (C) 2018 ORGANIZATION # This file is distributed under the same license as the PROJECT project. -# FIRST AUTHOR , 2013. +# FIRST AUTHOR , 2018. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2013-10-13 21:32+0200\n" +"POT-Creation-Date: 2018-05-15 21:43+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.6\n" +"Generated-By: Babel 2.5.3\n" -#: forms.py:22 -msgid "Select all" -msgstr "" - -#: forms.py:22 -msgid "Select none" -msgstr "" - -#: forms.py:61 +#: forms.py:46 msgid "Project name" msgstr "" -#: forms.py:62 forms.py:86 forms.py:102 +#: forms.py:47 forms.py:71 forms.py:88 msgid "Private code" msgstr "" -#: forms.py:63 +#: forms.py:48 msgid "Email" msgstr "" -#: forms.py:85 forms.py:101 forms.py:107 +#: forms.py:70 forms.py:87 forms.py:98 msgid "Project identifier" msgstr "" -#: forms.py:87 templates/send_invites.html:5 +#: forms.py:72 msgid "Create the project" msgstr "" -#: forms.py:92 +#: forms.py:77 msgid "" "The project identifier is used to log in and for the URL of the project. " "We tried to generate an identifier for you but a project with this " "identifier already exists. Please create a new identifier that you will " -"be able to remember." +"be able to remember" msgstr "" -#: forms.py:103 +#: forms.py:89 forms.py:94 msgid "Get in" msgstr "" -#: forms.py:108 +#: forms.py:93 +msgid "Admin password" +msgstr "" + +#: forms.py:99 msgid "Send me the code by email" msgstr "" -#: forms.py:112 +#: forms.py:103 msgid "This project does not exists" msgstr "" -#: forms.py:116 +#: forms.py:108 +msgid "Password mismatch" +msgstr "" + +#: forms.py:109 +msgid "Password" +msgstr "" + +#: forms.py:110 +msgid "Password confirmation" +msgstr "" + +#: forms.py:111 +msgid "Reset password" +msgstr "" + +#: forms.py:115 msgid "Date" msgstr "" -#: forms.py:117 +#: forms.py:116 msgid "What?" msgstr "" -#: forms.py:118 +#: forms.py:117 msgid "Payer" msgstr "" -#: forms.py:119 +#: forms.py:118 msgid "Amount paid" msgstr "" -#: forms.py:120 templates/list_bills.html:103 +#: forms.py:119 templates/forms.html:100 templates/list_bills.html:101 msgid "For whom?" msgstr "" -#: forms.py:122 +#: forms.py:121 msgid "Submit" msgstr "" -#: forms.py:123 +#: forms.py:122 msgid "Submit and add a new one" msgstr "" -#: forms.py:149 +#: forms.py:146 msgid "Bills can't be null" msgstr "" -#: forms.py:154 +#: forms.py:151 msgid "Name" msgstr "" -#: forms.py:155 templates/forms.html:95 +#: forms.py:152 +msgid "Weight" +msgstr "" + +#: forms.py:153 templates/forms.html:123 msgid "Add" msgstr "" -#: forms.py:163 +#: forms.py:162 msgid "User name incorrect" msgstr "" @@ -113,105 +129,151 @@ msgstr "" msgid "This project already have this member" msgstr "" -#: forms.py:178 +#: forms.py:183 msgid "People to notify" msgstr "" -#: forms.py:179 +#: forms.py:184 msgid "Send invites" msgstr "" -#: forms.py:185 +#: forms.py:190 #, python-format msgid "The email %(email)s is not valid" msgstr "" -#: forms.py:191 -msgid "Start date" +#: forms.py:196 +msgid "What do you want to download ?" msgstr "" -#: forms.py:192 -msgid "End date" +#: forms.py:199 +msgid "bills" msgstr "" -#: web.py:95 +#: forms.py:199 +msgid "transactions" +msgstr "" + +#: forms.py:201 +msgid "Export file format" +msgstr "" + +#: web.py:129 +msgid "Too many failed login attempts, please retry later." +msgstr "" + +#: web.py:144 +#, python-format +msgid "This admin password is not the right one. Only %(num)d attempts left." +msgstr "" + +#: web.py:167 +msgid "You either provided a bad token or no project identifier." +msgstr "" + +#: web.py:195 msgid "This private code is not the right one" msgstr "" -#: web.py:147 +#: web.py:242 #, python-format msgid "You have just created '%(project)s' to share your expenses" msgstr "" -#: web.py:165 +#: web.py:260 #, python-format msgid "%(msg_compl)sThe project identifier is %(project)s" msgstr "" -#: web.py:185 -msgid "a mail has been sent to you with the password" +#: web.py:281 +msgid "A link to reset your password has been sent to your email." msgstr "" -#: web.py:211 +#: web.py:291 +msgid "No token provided" +msgstr "" + +#: web.py:294 +msgid "Invalid token" +msgstr "" + +#: web.py:297 +msgid "Unknown project" +msgstr "" + +#: web.py:303 +msgid "Password successfully reset." +msgstr "" + +#: web.py:351 msgid "Project successfully deleted" msgstr "" -#: web.py:254 +#: web.py:401 #, python-format msgid "You have been invited to share your expenses for %(project)s" msgstr "" -#: web.py:261 +#: web.py:408 msgid "Your invitations have been sent" msgstr "" -#: web.py:290 +#: web.py:439 #, python-format msgid "%(member)s had been added" msgstr "" -#: web.py:303 +#: web.py:452 #, python-format msgid "%(name)s is part of this project again" msgstr "" -#: web.py:312 +#: web.py:461 #, python-format -msgid "User '%(name)s' has been deactivated" +msgid "" +"User '%(name)s' has been deactivated. It will still appear in the users " +"list until its balance becomes zero." msgstr "" -#: web.py:314 +#: web.py:465 #, python-format msgid "User '%(name)s' has been removed" msgstr "" -#: web.py:331 +#: web.py:480 +#, python-format +msgid "User '%(name)s' has been edited" +msgstr "" + +#: web.py:500 msgid "The bill has been added" msgstr "" -#: web.py:351 +#: web.py:520 msgid "The bill has been deleted" msgstr "" -#: web.py:369 +#: web.py:538 msgid "The bill has been modified" msgstr "" -#: templates/add_bill.html:9 +#: templates/add_bill.html:9 templates/edit_member.html:9 msgid "Back to the list" msgstr "" -#: templates/authenticate.html:6 -msgid "" -"The project you are trying to access do not exist, do you want \n" -"to" +#: templates/admin.html:10 +msgid "Administration tasks are currently disabled." msgstr "" -#: templates/authenticate.html:7 +#: templates/authenticate.html:6 +msgid "The project you are trying to access do not exist, do you want to" +msgstr "" + +#: templates/authenticate.html:8 msgid "create it" msgstr "" -#: templates/authenticate.html:7 +#: templates/authenticate.html:8 msgid "?" msgstr "" @@ -239,6 +301,24 @@ msgstr "" msgid "Oldest bill" msgstr "" +#: templates/dashboard.html:5 templates/list_bills.html:101 +msgid "Actions" +msgstr "" + +#: templates/dashboard.html:17 templates/list_bills.html:65 +#: templates/list_bills.html:111 +msgid "edit" +msgstr "" + +#: templates/dashboard.html:18 templates/forms.html:83 +#: templates/list_bills.html:112 +msgid "delete" +msgstr "" + +#: templates/dashboard.html:25 +msgid "The Dashboard is currently deactivated." +msgstr "" + #: templates/edit_project.html:6 templates/list_bills.html:24 msgid "you sure?" msgstr "" @@ -247,44 +327,59 @@ msgstr "" msgid "Edit this project" msgstr "" -#: templates/forms.html:23 +#: templates/edit_project.html:15 +msgid "Download this project's data" +msgstr "" + +#: templates/forms.html:27 msgid "Can't remember the password?" msgstr "" -#: templates/forms.html:26 +#: templates/forms.html:30 msgid "Cancel" msgstr "" -#: templates/forms.html:68 +#: templates/forms.html:82 msgid "Edit the project" msgstr "" -#: templates/forms.html:69 templates/list_bills.html:70 -#: templates/list_bills.html:114 -msgid "delete" -msgstr "" - -#: templates/forms.html:77 +#: templates/forms.html:91 msgid "Edit this bill" msgstr "" -#: templates/forms.html:77 templates/list_bills.html:94 +#: templates/forms.html:91 templates/list_bills.html:89 msgid "Add a bill" msgstr "" -#: templates/forms.html:95 -msgid "Type user name here" -msgstr "" - -#: templates/forms.html:102 -msgid "Send the invitations" +#: templates/forms.html:103 +msgid "Select all" msgstr "" #: templates/forms.html:103 +msgid "Select none" +msgstr "" + +#: templates/forms.html:122 +msgid "Type user name here" +msgstr "" + +#: templates/forms.html:129 +msgid "Edit this member" +msgstr "" + +#: templates/forms.html:145 +msgid "Send the invitations" +msgstr "" + +#: templates/forms.html:146 msgid "No, thanks" msgstr "" -#: templates/home.html:8 +#: templates/forms.html:157 +msgid "Download" +msgstr "" + +#: templates/home.html:7 msgid "Manage your shared
expenses, easily" msgstr "" @@ -292,39 +387,39 @@ msgstr "" msgid "Try out the demo" msgstr "" -#: templates/home.html:12 +#: templates/home.html:13 msgid "You're sharing a house?" msgstr "" -#: templates/home.html:12 +#: templates/home.html:13 msgid "Going on holidays with friends?" msgstr "" -#: templates/home.html:12 +#: templates/home.html:13 msgid "Simply sharing money with others?" msgstr "" -#: templates/home.html:12 +#: templates/home.html:13 msgid "We can help!" msgstr "" -#: templates/home.html:24 +#: templates/home.html:21 msgid "Log to an existing project" msgstr "" -#: templates/home.html:28 +#: templates/home.html:25 msgid "log in" msgstr "" -#: templates/home.html:29 +#: templates/home.html:26 msgid "can't remember your password?" msgstr "" -#: templates/home.html:36 +#: templates/home.html:34 templates/home.html:42 msgid "or create a new one" msgstr "" -#: templates/home.html:40 +#: templates/home.html:38 msgid "let's get started" msgstr "" @@ -338,91 +433,91 @@ msgstr "" msgid "Account manager" msgstr "" -#: templates/layout.html:45 templates/settle_bills.html:4 +#: templates/layout.html:39 msgid "Bills" msgstr "" -#: templates/layout.html:46 templates/settle_bills.html:5 +#: templates/layout.html:40 msgid "Settle" msgstr "" -#: templates/layout.html:53 +#: templates/layout.html:41 +msgid "Statistics" +msgstr "" + +#: templates/layout.html:48 msgid "options" msgstr "" -#: templates/layout.html:55 +#: templates/layout.html:50 msgid "Project settings" msgstr "" -#: templates/layout.html:59 +#: templates/layout.html:54 msgid "switch to" msgstr "" -#: templates/layout.html:62 +#: templates/layout.html:57 msgid "Start a new project" msgstr "" -#: templates/layout.html:64 +#: templates/layout.html:59 msgid "Logout" msgstr "" -#: templates/layout.html:92 +#: templates/layout.html:66 +msgid "Dashboard" +msgstr "" + +#: templates/layout.html:89 msgid "This is a free software" msgstr "" -#: templates/layout.html:92 +#: templates/layout.html:89 msgid "you can contribute and improve it!" msgstr "" -#: templates/list_bills.html:74 +#: templates/list_bills.html:63 +msgid "deactivate" +msgstr "" + +#: templates/list_bills.html:70 msgid "reactivate" msgstr "" -#: templates/list_bills.html:88 -msgid "The project identifier is" +#: templates/list_bills.html:82 +msgid "Invite people to join this project!" msgstr "" -#: templates/list_bills.html:88 -msgid "remember it!" -msgstr "" - -#: templates/list_bills.html:89 +#: templates/list_bills.html:83 msgid "Add a new bill" msgstr "" -#: templates/list_bills.html:103 +#: templates/list_bills.html:101 msgid "When?" msgstr "" -#: templates/list_bills.html:103 +#: templates/list_bills.html:101 msgid "Who paid?" msgstr "" -#: templates/list_bills.html:103 +#: templates/list_bills.html:101 msgid "For what?" msgstr "" -#: templates/list_bills.html:103 templates/settle_bills.html:31 +#: templates/list_bills.html:101 templates/settle_bills.html:22 msgid "How much?" msgstr "" -#: templates/list_bills.html:103 -msgid "Actions" -msgstr "" - -#: templates/list_bills.html:111 +#: templates/list_bills.html:109 msgid "each" msgstr "" -#: templates/list_bills.html:113 -msgid "edit" -msgstr "" - -#: templates/list_bills.html:122 +#: templates/list_bills.html:120 msgid "Nothing to list yet. You probably want to" msgstr "" -#: templates/list_bills.html:122 +#: templates/list_bills.html:120 msgid "add a bill" msgstr "" @@ -434,43 +529,50 @@ msgstr "" msgid "Your projects" msgstr "" -#: templates/send_invites.html:6 -msgid "Invite people" +#: templates/reset_password.html:7 +msgid "Reset your password" msgstr "" -#: templates/send_invites.html:7 -msgid "Use it!" -msgstr "" - -#: templates/send_invites.html:11 +#: templates/send_invites.html:4 msgid "Invite people to join this project" msgstr "" -#: templates/send_invites.html:12 +#: templates/send_invites.html:5 msgid "" -"Specify a (coma separated) list of email adresses you want to notify " -"about the \n" +"Specify a (comma separated) list of email adresses you want to notify " +"about the\n" "creation of this budget management project and we will send them an email" " for you." msgstr "" -#: templates/send_invites.html:14 -msgid "If you prefer, you can" +#: templates/send_invites.html:7 +msgid "" +"If you prefer, you can share the project identifier and the shared\n" +"password by other communication means. Or even directly share the " +"following link:" msgstr "" -#: templates/send_invites.html:14 -msgid "skip this step" -msgstr "" - -#: templates/send_invites.html:14 -msgid "and notify them yourself" -msgstr "" - -#: templates/settle_bills.html:31 +#: templates/settle_bills.html:22 msgid "Who pays?" msgstr "" -#: templates/settle_bills.html:31 +#: templates/settle_bills.html:22 msgid "To whom?" msgstr "" +#: templates/statistics.html:21 +msgid "Who?" +msgstr "" + +#: templates/statistics.html:21 +msgid "Paid" +msgstr "" + +#: templates/statistics.html:21 +msgid "Spent" +msgstr "" + +#: templates/statistics.html:21 +msgid "Balance" +msgstr "" + From fb31868278af06322273ad94c6e58eaa32bb5823 Mon Sep 17 00:00:00 2001 From: Adrien CLERC Date: Tue, 15 May 2018 21:47:01 +0200 Subject: [PATCH 15/21] update locales from template with pybabel --- .../translations/fr/LC_MESSAGES/messages.po | 491 ++++++++++-------- 1 file changed, 264 insertions(+), 227 deletions(-) diff --git a/ihatemoney/translations/fr/LC_MESSAGES/messages.po b/ihatemoney/translations/fr/LC_MESSAGES/messages.po index b3440987..334fa8e5 100644 --- a/ihatemoney/translations/fr/LC_MESSAGES/messages.po +++ b/ihatemoney/translations/fr/LC_MESSAGES/messages.po @@ -7,129 +7,122 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2013-10-13 21:32+0200\n" +"POT-Creation-Date: 2018-05-15 21:43+0200\n" "PO-Revision-Date: 2011-10-14 23:51+0200\n" "Last-Translator: Quentin Roy \n" +"Language: fr\n" "Language-Team: fr \n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.6\n" +"Generated-By: Babel 2.5.3\n" -#: forms.py:22 -msgid "Select all" -msgstr "Tout cocher" - -#: forms.py:22 -msgid "Select none" -msgstr "Tout décocher" - -#: forms.py:61 +#: forms.py:46 msgid "Project name" msgstr "Nom de projet" -#: forms.py:62 forms.py:86 forms.py:102 +#: forms.py:47 forms.py:71 forms.py:88 msgid "Private code" msgstr "Code d'accès" -#: forms.py:63 +#: forms.py:48 msgid "Email" msgstr "Email" -#: forms.py:85 forms.py:101 forms.py:107 +#: forms.py:70 forms.py:87 forms.py:98 msgid "Project identifier" msgstr "Identifiant du projet" -#: forms.py:87 -msgid "Admin password" -msgstr "Mot de passe administrateur" - -#: forms.py:87 templates/send_invites.html:5 +#: forms.py:72 msgid "Create the project" msgstr "Créer le projet" -#: forms.py:92 +#: forms.py:77 msgid "" "The project identifier is used to log in and for the URL of the project. " "We tried to generate an identifier for you but a project with this " "identifier already exists. Please create a new identifier that you will " -"be able to remember." +"be able to remember" msgstr "" -"L'identifiant du projet est utilisé pour se connecter." -"Nous avons essayé de générer un identifiant mais " -"celui ci existe déjà. Merci de créer un nouvel identifiant que vous serez" -" capable de retenir" -#: forms.py:103 +#: forms.py:89 forms.py:94 msgid "Get in" msgstr "Entrer" -#: forms.py:107 +#: forms.py:93 +msgid "Admin password" +msgstr "Mot de passe administrateur" + +#: forms.py:99 +msgid "Send me the code by email" +msgstr "Envoyez moi le code par email" + +#: forms.py:103 +msgid "This project does not exists" +msgstr "Ce projet n'existe pas" + +#: forms.py:108 msgid "Password mismatch" msgstr "Les mots de passe fournis ne sont pas les mêmes." #: forms.py:109 -msgid "Password confirmation" -msgstr "Confirmation du mot de passe" - -#: forms.py:107 msgid "Password" msgstr "Mot de passe" -#: forms.py:108 -msgid "Send me the code by email" -msgstr "Envoyez moi le code par email" +#: forms.py:110 +msgid "Password confirmation" +msgstr "Confirmation du mot de passe" -#: forms.py:112 -msgid "This project does not exists" -msgstr "Ce projet n'existe pas" +#: forms.py:111 +msgid "Reset password" +msgstr "" -#: forms.py:116 +#: forms.py:115 msgid "Date" msgstr "Date" -#: forms.py:117 +#: forms.py:116 msgid "What?" msgstr "Quoi ?" -#: forms.py:118 +#: forms.py:117 msgid "Payer" msgstr "Payeur" -#: forms.py:119 +#: forms.py:118 msgid "Amount paid" msgstr "Montant" -#: forms.py:120 templates/list_bills.html:103 +#: forms.py:119 templates/forms.html:100 templates/list_bills.html:101 msgid "For whom?" msgstr "Pour qui ?" -#: forms.py:122 +#: forms.py:121 msgid "Submit" msgstr "Valider" -#: forms.py:123 +#: forms.py:122 msgid "Submit and add a new one" msgstr "Valider et ajouter une autre facture" -#: forms.py:149 +#: forms.py:146 msgid "Bills can't be null" msgstr "Le montant d'une facture ne peut pas être nul." -#: forms.py:154 +#: forms.py:151 msgid "Name" msgstr "Nom" -#: forms.py:155 +#: forms.py:152 msgid "Weight" msgstr "Parts" -#: forms.py:155 templates/forms.html:95 +#: forms.py:153 templates/forms.html:123 msgid "Add" msgstr "Ajouter" -#: forms.py:163 +#: forms.py:162 msgid "User name incorrect" msgstr "Nom d'utilisateur incorrect" @@ -137,159 +130,158 @@ msgstr "Nom d'utilisateur incorrect" msgid "This project already have this member" msgstr "Ce membre existe déjà pour ce projet" -#: forms.py:178 +#: forms.py:183 msgid "People to notify" msgstr "Personnes à prévenir" -#: forms.py:179 +#: forms.py:184 msgid "Send invites" msgstr "Envoyer les invitations" -#: forms.py:185 +#: forms.py:190 #, python-format msgid "The email %(email)s is not valid" msgstr "L'email %(email)s est invalide" -#: forms.py:191 -msgid "Start date" -msgstr "Date de départ" - -#: forms.py:192 -msgid "End date" -msgstr "Date de fin" - -#: forms.py:202 +#: forms.py:196 msgid "What do you want to download ?" msgstr "Que voulez-vous télécharger ?" -#: forms.py:205 +#: forms.py:199 msgid "bills" msgstr "factures" -#: forms.py:205 +#: forms.py:199 msgid "transactions" msgstr "remboursements" -#: forms.py:206 +#: forms.py:201 msgid "Export file format" msgstr "Format du fichier d'export" -#: web.py:95 -msgid "You either provided a bad token or no project identifier." -msgstr "L'identifiant du projet ou le token fourni n'est pas correct." - -#: web.py:95 -msgid "This private code is not the right one" -msgstr "Le code que vous avez entré n'est pas correct" - -#: web.py:106 -msgid "This admin password is not the right one. Only %(num)d attempts left." -msgstr "Le mot de passe administrateur que vous avez entré n'est pas correct. Plus que %(num)d tentatives." - -#: web.py:106 +#: web.py:129 msgid "Too many failed login attempts, please retry later." msgstr "Trop d'échecs d'authentification successifs, veuillez réessayer plus tard." -#: web.py:147 +#: web.py:144 +#, python-format +msgid "This admin password is not the right one. Only %(num)d attempts left." +msgstr "" +"Le mot de passe administrateur que vous avez entré n'est pas correct. " +"Plus que %(num)d tentatives." + +#: web.py:167 +msgid "You either provided a bad token or no project identifier." +msgstr "L'identifiant du projet ou le token fourni n'est pas correct." + +#: web.py:195 +msgid "This private code is not the right one" +msgstr "Le code que vous avez entré n'est pas correct" + +#: web.py:242 #, python-format msgid "You have just created '%(project)s' to share your expenses" msgstr "Vous venez de créer '%(project)s' pour partager vos dépenses" -#: web.py:165 +#: web.py:260 #, python-format msgid "%(msg_compl)sThe project identifier is %(project)s" msgstr "L'identifiant de ce projet est '%(project)s'" -#: web.py:185 +#: web.py:281 msgid "A link to reset your password has been sent to your email." msgstr "Un lien pour changer votre mot de passe vous a été envoyé par mail." -#: web.py:211 +#: web.py:291 +msgid "No token provided" +msgstr "" + +#: web.py:294 +msgid "Invalid token" +msgstr "Token invalide" + +#: web.py:297 +msgid "Unknown project" +msgstr "Project inconnu" + +#: web.py:303 +msgid "Password successfully reset." +msgstr "Le mot de passe a été changé avec succès." + +#: web.py:351 msgid "Project successfully deleted" msgstr "Projet supprimé" -#: web.py:254 +#: web.py:401 #, python-format msgid "You have been invited to share your expenses for %(project)s" msgstr "Vous avez été invité à partager vos dépenses pour %(project)s" -#: web.py:259 -#, python-format -msgid ""No token provided"" -msgstr "Aucun token n'a été fourni." - -#: web.py:259 -#, python-format -msgid "Unknown project" -msgstr "Project inconnu" - -#: web.py:261 -#, python-format -msgid "Invalid token" -msgstr "Token invalide" - -#: web.py:267 -#, python-format -msgid "Password successfully reset." -msgstr "Le mot de passe a été changé avec succès." - -#: web.py:261 +#: web.py:408 msgid "Your invitations have been sent" msgstr "Vos invitations ont bien été envoyées" -#: web.py:290 +#: web.py:439 #, python-format msgid "%(member)s had been added" msgstr "%(member)s a bien été ajouté" -#: web.py:303 +#: web.py:452 #, python-format msgid "%(name)s is part of this project again" msgstr "%(name)s a rejoint le projet" -#: web.py:312 +#: web.py:461 #, python-format -msgid "User '%(name)s' has been deactivated" -msgstr "Le membre '%(name)s' a été désactivé" +msgid "" +"User '%(name)s' has been deactivated. It will still appear in the users " +"list until its balance becomes zero." +msgstr "" +"Le membre '%(name)s' a été désactivé. Il continuera d'apparaître jusqu'à " +"ce que sa balance devienne égale à zéro." -#: web.py:314 +#: web.py:465 #, python-format -msgid "User '%(name)s' has been deactivated. It will still appear in the users list until its balance becomes zero." -msgstr "Le membre '%(name)s' a été désactivé. Il continuera d'apparaître jusqu'à ce que sa balance devienne égale à zéro." +msgid "User '%(name)s' has been removed" +msgstr "" -#: web.py:331 +#: web.py:480 +#, python-format +msgid "User '%(name)s' has been edited" +msgstr "" + +#: web.py:500 msgid "The bill has been added" msgstr "La facture a bien été ajoutée" -#: web.py:351 +#: web.py:520 msgid "The bill has been deleted" msgstr "La facture a été supprimée" -#: web.py:369 +#: web.py:538 msgid "The bill has been modified" msgstr "La facture a été modifiée" -#: templates/add_bill.html:9 +#: templates/add_bill.html:9 templates/edit_member.html:9 msgid "Back to the list" msgstr "Retourner à la liste" +#: templates/admin.html:10 +msgid "Administration tasks are currently disabled." +msgstr "Les tâches d'administration sont actuellement désactivées." + #: templates/authenticate.html:6 -msgid "" -"The project you are trying to access do not exist, do you want to" +msgid "The project you are trying to access do not exist, do you want to" msgstr "Le projet auquel vous essayez d'acceder n'existe pas. Souhaitez vous" -#: templates/authenticate.html:7 +#: templates/authenticate.html:8 msgid "create it" msgstr "le créer" -#: templates/authenticate.html:7 +#: templates/authenticate.html:8 msgid "?" msgstr " ?" -#: templates/authenticate.html:7 -msgid "Administration tasks are currently disabled." -msgstr "Les tâches d'administration sont actuellement désactivées." - #: templates/create_project.html:4 msgid "Create a new project" msgstr "Créer un nouveau projet" @@ -314,6 +306,20 @@ msgstr "Facture la plus récente" msgid "Oldest bill" msgstr "Facture la plus ancienne" +#: templates/dashboard.html:5 templates/list_bills.html:101 +msgid "Actions" +msgstr "Actions" + +#: templates/dashboard.html:17 templates/list_bills.html:65 +#: templates/list_bills.html:111 +msgid "edit" +msgstr "éditer" + +#: templates/dashboard.html:18 templates/forms.html:83 +#: templates/list_bills.html:112 +msgid "delete" +msgstr "supprimer" + #: templates/dashboard.html:25 msgid "The Dashboard is currently deactivated." msgstr "La page d'administration est actuellement désactivée." @@ -326,60 +332,59 @@ msgstr "c'est sûr ?" msgid "Edit this project" msgstr "Éditer ce projet" -#: templates/forms.html:23 -msgid "Can't remember the password?" -msgstr "Vous ne vous souvenez plus du code d'accès ?" - -#: templates/forms.html:26 -msgid "Cancel" -msgstr "Annuler" - -#: templates/forms.html:68 -msgid "Edit the project" -msgstr "Éditer le projet" - -#: templates/list_bills.html:70 -msgid "deactivate" -msgstr "désactiver" - -#: templates/forms.html:69 templates/list_bills.html:70 -#: templates/list_bills.html:114 -msgid "delete" -msgstr "supprimer" - -#: templates/forms.html:77 -msgid "Edit this bill" -msgstr "Éditer cette facture" - -#: templates/forms.html:77 templates/list_bills.html:94 -msgid "Add a bill" -msgstr "Ajouter une facture" - -#: templates/forms.html:95 -msgid "Type user name here" -msgstr "Nouveau participant" - -#: templates/forms.html:100 -msgid "Edit this member" -msgstr "Éditer ce participant" - -#: templates/forms.html:102 -msgid "Send the invitations" -msgstr "Envoyer les invitations" - -#: templates/forms.html:103 -msgid "No, thanks" -msgstr "Non merci" - -#: templates/forms.html:136 +#: templates/edit_project.html:15 msgid "Download this project's data" msgstr "Télécharger les données de ce projet" -#: templates/forms.html:136 +#: templates/forms.html:27 +msgid "Can't remember the password?" +msgstr "Vous ne vous souvenez plus du code d'accès ?" + +#: templates/forms.html:30 +msgid "Cancel" +msgstr "Annuler" + +#: templates/forms.html:82 +msgid "Edit the project" +msgstr "Éditer le projet" + +#: templates/forms.html:91 +msgid "Edit this bill" +msgstr "Éditer cette facture" + +#: templates/forms.html:91 templates/list_bills.html:89 +msgid "Add a bill" +msgstr "Ajouter une facture" + +#: templates/forms.html:103 +msgid "Select all" +msgstr "Tout cocher" + +#: templates/forms.html:103 +msgid "Select none" +msgstr "Tout décocher" + +#: templates/forms.html:122 +msgid "Type user name here" +msgstr "Nouveau participant" + +#: templates/forms.html:129 +msgid "Edit this member" +msgstr "Éditer ce participant" + +#: templates/forms.html:145 +msgid "Send the invitations" +msgstr "Envoyer les invitations" + +#: templates/forms.html:146 +msgid "No, thanks" +msgstr "Non merci" + +#: templates/forms.html:157 msgid "Download" msgstr "Télécharger" -#: templates/home.html:8 +#: templates/home.html:7 msgid "Manage your shared
expenses, easily" msgstr "Gérez vos dépenses
partagées, facilement" @@ -387,39 +392,39 @@ msgstr "Gérez vos dépenses
partagées, facilement" msgid "Try out the demo" msgstr "Essayez la démo" -#: templates/home.html:12 +#: templates/home.html:13 msgid "You're sharing a house?" msgstr "Vous êtes en colocation ?" -#: templates/home.html:12 +#: templates/home.html:13 msgid "Going on holidays with friends?" msgstr "Partez en vacances avec des amis ?" -#: templates/home.html:12 +#: templates/home.html:13 msgid "Simply sharing money with others?" msgstr "Ça vous arrive de partager de l'argent avec d'autres ?" -#: templates/home.html:12 +#: templates/home.html:13 msgid "We can help!" msgstr "On peut vous aider !" -#: templates/home.html:24 +#: templates/home.html:21 msgid "Log to an existing project" msgstr "Se connecter à un projet existant" -#: templates/home.html:28 +#: templates/home.html:25 msgid "log in" msgstr "se connecter" -#: templates/home.html:29 +#: templates/home.html:26 msgid "can't remember your password?" msgstr "vous ne vous souvenez plus du code d'accès ?" -#: templates/home.html:36 +#: templates/home.html:34 templates/home.html:42 msgid "or create a new one" msgstr "ou créez en un nouveau" -#: templates/home.html:40 +#: templates/home.html:38 msgid "let's get started" msgstr "c'est parti !" @@ -435,95 +440,91 @@ msgstr "" msgid "Account manager" msgstr "Gestion de comptes" -#: templates/layout.html:45 templates/settle_bills.html:4 +#: templates/layout.html:39 msgid "Bills" msgstr "Factures" -#: templates/layout.html:46 templates/settle_bills.html:5 +#: templates/layout.html:40 msgid "Settle" msgstr "Remboursements" -#: templates/layout.html:50 +#: templates/layout.html:41 msgid "Statistics" msgstr "Statistiques" -#: templates/layout.html:53 +#: templates/layout.html:48 msgid "options" msgstr "options" -#: templates/layout.html:55 +#: templates/layout.html:50 msgid "Project settings" msgstr "Options du projet" -#: templates/layout.html:59 +#: templates/layout.html:54 msgid "switch to" msgstr "aller à" -#: templates/layout.html:62 +#: templates/layout.html:57 msgid "Start a new project" msgstr "Nouveau projet" -#: templates/layout.html:64 +#: templates/layout.html:59 msgid "Logout" msgstr "Se déconnecter" -#: templates/layout.html:92 +#: templates/layout.html:66 +msgid "Dashboard" +msgstr "" + +#: templates/layout.html:89 msgid "This is a free software" msgstr "Ceci est un logiciel libre" -#: templates/layout.html:92 +#: templates/layout.html:89 msgid "you can contribute and improve it!" msgstr "vous pouvez y contribuer et l'améliorer" -#: templates/list_bills.html:74 +#: templates/list_bills.html:63 +msgid "deactivate" +msgstr "désactiver" + +#: templates/list_bills.html:70 msgid "reactivate" msgstr "ré-activer" -#: templates/list_bills.html:88 -msgid "Invite" -msgstr "Invitez" - -#: templates/list_bills.html:88 +#: templates/list_bills.html:82 msgid "Invite people to join this project!" msgstr "Invitez d'autres personnes à rejoindre ce projet !" -#: templates/list_bills.html:89 +#: templates/list_bills.html:83 msgid "Add a new bill" msgstr "Nouvelle facture" -#: templates/list_bills.html:103 +#: templates/list_bills.html:101 msgid "When?" msgstr "Quand ?" -#: templates/list_bills.html:103 +#: templates/list_bills.html:101 msgid "Who paid?" msgstr "Qui a payé ?" -#: templates/list_bills.html:103 +#: templates/list_bills.html:101 msgid "For what?" msgstr "Pour quoi ?" -#: templates/list_bills.html:103 templates/settle_bills.html:31 +#: templates/list_bills.html:101 templates/settle_bills.html:22 msgid "How much?" msgstr "Combien ?" -#: templates/list_bills.html:103 -msgid "Actions" -msgstr "Actions" - -#: templates/list_bills.html:111 +#: templates/list_bills.html:109 msgid "each" msgstr "chacun" -#: templates/list_bills.html:113 -msgid "edit" -msgstr "éditer" - -#: templates/list_bills.html:122 +#: templates/list_bills.html:120 msgid "Nothing to list yet. You probably want to" msgstr "Rien à lister pour l'instant. Vous voulez surement" -#: templates/list_bills.html:122 +#: templates/list_bills.html:120 msgid "add a bill" msgstr "ajouter une facture" @@ -535,15 +536,15 @@ msgstr "Rappel du code d'accès" msgid "Your projects" msgstr "Vos projets" -#: templates/reset_password.html:2 +#: templates/reset_password.html:7 msgid "Reset your password" msgstr "Changez votre mot de passe" -#: templates/send_invites.html:11 +#: templates/send_invites.html:4 msgid "Invite people to join this project" msgstr "Invitez des personnes à rejoindre ce projet" -#: templates/send_invites.html:12 +#: templates/send_invites.html:5 msgid "" "Specify a (comma separated) list of email adresses you want to notify " "about the\n" @@ -553,33 +554,69 @@ msgstr "" "Entrez les addresses des personnes que vous souhaitez inviter, séparées " "par des virgules. On s'occupe de leur envoyer un email." -#: templates/send_invites.html:14 -msgid "If you prefer, you can share the project identifier and the shared\n" -"password by other communication means. Or even directly share the following link:" -msgstr "Si vous préférez vous pouvez partager l'identifiant du projet et son mot " -"de passe par un autre moyen de communication. Ou directement partager le lien " -"suivant :" +#: templates/send_invites.html:7 +msgid "" +"If you prefer, you can share the project identifier and the shared\n" +"password by other communication means. Or even directly share the " +"following link:" +msgstr "" +"Si vous préférez vous pouvez partager l'identifiant du projet et son mot " +"de passe par un autre moyen de communication. Ou directement partager le " +"lien suivant :" -#: templates/settle_bills.html:31 +#: templates/settle_bills.html:22 msgid "Who pays?" msgstr "Qui doit payer ?" -#: templates/settle_bills.html:31 +#: templates/settle_bills.html:22 msgid "To whom?" msgstr "Pour qui ?" -#: templates/statistics.html:22 +#: templates/statistics.html:21 msgid "Who?" msgstr "Qui ?" -#: templates/statistics.html:22 +#: templates/statistics.html:21 msgid "Paid" msgstr "A payé" -#: templates/statistics.html:22 +#: templates/statistics.html:21 msgid "Spent" msgstr "A dépensé" -#: templates/statistics.html:22 +#: templates/statistics.html:21 msgid "Balance" msgstr "Solde" + +#~ msgid "" +#~ "The project identifier is used to " +#~ "log in and for the URL of " +#~ "the project. We tried to generate " +#~ "an identifier for you but a " +#~ "project with this identifier already " +#~ "exists. Please create a new identifier" +#~ " that you will be able to " +#~ "remember." +#~ msgstr "" +#~ "L'identifiant du projet est utilisé pour" +#~ " se connecter.Nous avons essayé de " +#~ "générer un identifiant mais celui ci " +#~ "existe déjà. Merci de créer un " +#~ "nouvel identifiant que vous serez " +#~ "capable de retenir" + +#~ msgid "Start date" +#~ msgstr "Date de départ" + +#~ msgid "End date" +#~ msgstr "Date de fin" + +#~ msgid "\"No token provided\"" +#~ msgstr "Aucun token n'a été fourni." + +#~ msgid "User '%(name)s' has been deactivated" +#~ msgstr "Le membre '%(name)s' a été désactivé" + +#~ msgid "Invite" +#~ msgstr "Invitez" + From 1947a5ae785ab17d5da09548819f1e4ba6b0d85a Mon Sep 17 00:00:00 2001 From: Adrien CLERC Date: Tue, 15 May 2018 21:50:31 +0200 Subject: [PATCH 16/21] update fr l10n --- .../translations/fr/LC_MESSAGES/messages.po | 103 +++++++++--------- 1 file changed, 53 insertions(+), 50 deletions(-) diff --git a/ihatemoney/translations/fr/LC_MESSAGES/messages.po b/ihatemoney/translations/fr/LC_MESSAGES/messages.po index 334fa8e5..5179f605 100644 --- a/ihatemoney/translations/fr/LC_MESSAGES/messages.po +++ b/ihatemoney/translations/fr/LC_MESSAGES/messages.po @@ -2,20 +2,21 @@ # Copyright (C) 2011 ORGANIZATION # This file is distributed under the same license as the PROJECT project. # Alexis Métaireau , 2011. -# +# Adrien CLERC, 2018. msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2018-05-15 21:43+0200\n" -"PO-Revision-Date: 2011-10-14 23:51+0200\n" -"Last-Translator: Quentin Roy \n" -"Language: fr\n" +"PO-Revision-Date: 2018-05-15 22:00+0200\n" +"Last-Translator: Adrien CLERC <>\n" "Language-Team: fr \n" -"Plural-Forms: nplurals=2; plural=(n > 1)\n" +"Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"X-Generator: Virtaal 0.7.1\n" "Generated-By: Babel 2.5.3\n" #: forms.py:46 @@ -24,7 +25,7 @@ msgstr "Nom de projet" #: forms.py:47 forms.py:71 forms.py:88 msgid "Private code" -msgstr "Code d'accès" +msgstr "Code d’accès" #: forms.py:48 msgid "Email" @@ -45,6 +46,9 @@ msgid "" "identifier already exists. Please create a new identifier that you will " "be able to remember" msgstr "" +"L’identifiant du projet est utilisé pour se connecter et pour l’URL du " +"projet. Nous avons essayé de générer un identifiant mais celui ci existe " +"déjà. Merci de créer un nouvel identifiant que vous serez capable de retenir" #: forms.py:89 forms.py:94 msgid "Get in" @@ -60,11 +64,11 @@ msgstr "Envoyez moi le code par email" #: forms.py:103 msgid "This project does not exists" -msgstr "Ce projet n'existe pas" +msgstr "Ce projet n’existe pas" #: forms.py:108 msgid "Password mismatch" -msgstr "Les mots de passe fournis ne sont pas les mêmes." +msgstr "Les mots de passe sont différents" #: forms.py:109 msgid "Password" @@ -76,7 +80,7 @@ msgstr "Confirmation du mot de passe" #: forms.py:111 msgid "Reset password" -msgstr "" +msgstr "Réinitialiser le mot de passe" #: forms.py:115 msgid "Date" @@ -108,7 +112,7 @@ msgstr "Valider et ajouter une autre facture" #: forms.py:146 msgid "Bills can't be null" -msgstr "Le montant d'une facture ne peut pas être nul." +msgstr "Le montant d’une facture ne peut pas être nul" #: forms.py:151 msgid "Name" @@ -124,7 +128,7 @@ msgstr "Ajouter" #: forms.py:162 msgid "User name incorrect" -msgstr "Nom d'utilisateur incorrect" +msgstr "Nom d’utilisateur incorrect" #: forms.py:167 msgid "This project already have this member" @@ -141,11 +145,11 @@ msgstr "Envoyer les invitations" #: forms.py:190 #, python-format msgid "The email %(email)s is not valid" -msgstr "L'email %(email)s est invalide" +msgstr "L’email %(email)s est invalide" #: forms.py:196 msgid "What do you want to download ?" -msgstr "Que voulez-vous télécharger ?" +msgstr "Que voulez-vous télécharger ?" #: forms.py:199 msgid "bills" @@ -157,36 +161,36 @@ msgstr "remboursements" #: forms.py:201 msgid "Export file format" -msgstr "Format du fichier d'export" +msgstr "Format du fichier d’export" #: web.py:129 msgid "Too many failed login attempts, please retry later." -msgstr "Trop d'échecs d'authentification successifs, veuillez réessayer plus tard." +msgstr "Trop d'échecs d’authentification successifs, veuillez réessayer plus tard." #: web.py:144 #, python-format msgid "This admin password is not the right one. Only %(num)d attempts left." msgstr "" -"Le mot de passe administrateur que vous avez entré n'est pas correct. " +"Le mot de passe administrateur que vous avez entré n’est pas correct. " "Plus que %(num)d tentatives." #: web.py:167 msgid "You either provided a bad token or no project identifier." -msgstr "L'identifiant du projet ou le token fourni n'est pas correct." +msgstr "L’identifiant du projet ou le token fourni n’est pas correct." #: web.py:195 msgid "This private code is not the right one" -msgstr "Le code que vous avez entré n'est pas correct" +msgstr "Le code que vous avez entré n’est pas correct" #: web.py:242 #, python-format msgid "You have just created '%(project)s' to share your expenses" -msgstr "Vous venez de créer '%(project)s' pour partager vos dépenses" +msgstr "Vous venez de créer « %(project)s » pour partager vos dépenses" #: web.py:260 #, python-format msgid "%(msg_compl)sThe project identifier is %(project)s" -msgstr "L'identifiant de ce projet est '%(project)s'" +msgstr "%(msg_compl)sL’identifiant de ce projet est %(project)s" #: web.py:281 msgid "A link to reset your password has been sent to your email." @@ -194,7 +198,7 @@ msgstr "Un lien pour changer votre mot de passe vous a été envoyé par mail." #: web.py:291 msgid "No token provided" -msgstr "" +msgstr "Aucun token n’a été fourni" #: web.py:294 msgid "Invalid token" @@ -237,18 +241,18 @@ msgid "" "User '%(name)s' has been deactivated. It will still appear in the users " "list until its balance becomes zero." msgstr "" -"Le membre '%(name)s' a été désactivé. Il continuera d'apparaître jusqu'à " -"ce que sa balance devienne égale à zéro." +"Le membre « %(name)s » a été désactivé. Il continuera d’apparaître jusqu'à " +"ce que sa balance soit nulle." #: web.py:465 #, python-format msgid "User '%(name)s' has been removed" -msgstr "" +msgstr "Le membre « %(name)s » a été supprimé" #: web.py:480 #, python-format msgid "User '%(name)s' has been edited" -msgstr "" +msgstr "Le membre « %(name)s » a été édité" #: web.py:500 msgid "The bill has been added" @@ -268,11 +272,11 @@ msgstr "Retourner à la liste" #: templates/admin.html:10 msgid "Administration tasks are currently disabled." -msgstr "Les tâches d'administration sont actuellement désactivées." +msgstr "Les tâches d’administration sont actuellement désactivées." #: templates/authenticate.html:6 msgid "The project you are trying to access do not exist, do you want to" -msgstr "Le projet auquel vous essayez d'acceder n'existe pas. Souhaitez vous" +msgstr "Le projet auquel vous essayez d’accéder n’existe pas, souhaitez vous" #: templates/authenticate.html:8 msgid "create it" @@ -280,7 +284,7 @@ msgstr "le créer" #: templates/authenticate.html:8 msgid "?" -msgstr " ?" +msgstr " ?" #: templates/create_project.html:4 msgid "Create a new project" @@ -322,11 +326,11 @@ msgstr "supprimer" #: templates/dashboard.html:25 msgid "The Dashboard is currently deactivated." -msgstr "La page d'administration est actuellement désactivée." +msgstr "Le tableau de bord est actuellement désactivée." #: templates/edit_project.html:6 templates/list_bills.html:24 msgid "you sure?" -msgstr "c'est sûr ?" +msgstr "c’est sûr ?" #: templates/edit_project.html:11 msgid "Edit this project" @@ -338,7 +342,7 @@ msgstr "Télécharger les données de ce projet" #: templates/forms.html:27 msgid "Can't remember the password?" -msgstr "Vous ne vous souvenez plus du code d'accès ?" +msgstr "Vous ne vous souvenez plus du code d’accès ?" #: templates/forms.html:30 msgid "Cancel" @@ -386,7 +390,7 @@ msgstr "Télécharger" #: templates/home.html:7 msgid "Manage your shared
expenses, easily" -msgstr "Gérez vos dépenses
partagées, facilement" +msgstr "Gérez vos dépenses
partagées, facilement" #: templates/home.html:9 msgid "Try out the demo" @@ -402,7 +406,7 @@ msgstr "Partez en vacances avec des amis ?" #: templates/home.html:13 msgid "Simply sharing money with others?" -msgstr "Ça vous arrive de partager de l'argent avec d'autres ?" +msgstr "Ça vous arrive de partager de l’argent avec d’autres ?" #: templates/home.html:13 msgid "We can help!" @@ -418,7 +422,7 @@ msgstr "se connecter" #: templates/home.html:26 msgid "can't remember your password?" -msgstr "vous ne vous souvenez plus du code d'accès ?" +msgstr "vous ne vous souvenez plus du code d’accès ?" #: templates/home.html:34 templates/home.html:42 msgid "or create a new one" @@ -426,15 +430,15 @@ msgstr "ou créez en un nouveau" #: templates/home.html:38 msgid "let's get started" -msgstr "c'est parti !" +msgstr "c’est parti !" #: templates/home.html:51 msgid "" "This access code will be sent to your friends. It is stored as-is by the " "server, so don\\'t reuse a personal password!" msgstr "" -"Ce code d\\'accès va être envoyé à vos amis et stocké en clair sur le " -"serveur.N\\'utilisez pas un mot de passe personnel !" +"Ce code d’accès va être envoyé à vos amis et stocké en clair sur le serveur. " +"N’utilisez pas un mot de passe personnel !" #: templates/layout.html:5 msgid "Account manager" @@ -474,7 +478,7 @@ msgstr "Se déconnecter" #: templates/layout.html:66 msgid "Dashboard" -msgstr "" +msgstr "Tableau de bord" #: templates/layout.html:89 msgid "This is a free software" @@ -482,7 +486,7 @@ msgstr "Ceci est un logiciel libre" #: templates/layout.html:89 msgid "you can contribute and improve it!" -msgstr "vous pouvez y contribuer et l'améliorer" +msgstr "vous pouvez y contribuer et l’améliorer !" #: templates/list_bills.html:63 msgid "deactivate" @@ -494,7 +498,7 @@ msgstr "ré-activer" #: templates/list_bills.html:82 msgid "Invite people to join this project!" -msgstr "Invitez d'autres personnes à rejoindre ce projet !" +msgstr "Invitez d’autres personnes à rejoindre ce projet !" #: templates/list_bills.html:83 msgid "Add a new bill" @@ -522,7 +526,7 @@ msgstr "chacun" #: templates/list_bills.html:120 msgid "Nothing to list yet. You probably want to" -msgstr "Rien à lister pour l'instant. Vous voulez surement" +msgstr "Rien à lister pour l’instant. Vous voulez surement" #: templates/list_bills.html:120 msgid "add a bill" @@ -530,7 +534,7 @@ msgstr "ajouter une facture" #: templates/password_reminder.html:4 msgid "Password reminder" -msgstr "Rappel du code d'accès" +msgstr "Rappel du code d’accès" #: templates/recent_projects.html:2 msgid "Your projects" @@ -551,8 +555,8 @@ msgid "" "creation of this budget management project and we will send them an email" " for you." msgstr "" -"Entrez les addresses des personnes que vous souhaitez inviter, séparées " -"par des virgules. On s'occupe de leur envoyer un email." +"Entrez les adresses des personnes que vous souhaitez inviter,\n" +"séparées par des virgules, on s’occupe de leur envoyer un email." #: templates/send_invites.html:7 msgid "" @@ -560,9 +564,9 @@ msgid "" "password by other communication means. Or even directly share the " "following link:" msgstr "" -"Si vous préférez vous pouvez partager l'identifiant du projet et son mot " -"de passe par un autre moyen de communication. Ou directement partager le " -"lien suivant :" +"Si vous préférez vous pouvez partager l’identifiant du projet\n" +"et son mot de passe par un autre moyen de communication. Ou directement " +"partager le lien suivant :" #: templates/settle_bills.html:22 msgid "Who pays?" @@ -598,7 +602,7 @@ msgstr "Solde" #~ " that you will be able to " #~ "remember." #~ msgstr "" -#~ "L'identifiant du projet est utilisé pour" +#~ "L’identifiant du projet est utilisé pour" #~ " se connecter.Nous avons essayé de " #~ "générer un identifiant mais celui ci " #~ "existe déjà. Merci de créer un " @@ -612,11 +616,10 @@ msgstr "Solde" #~ msgstr "Date de fin" #~ msgid "\"No token provided\"" -#~ msgstr "Aucun token n'a été fourni." +#~ msgstr "Aucun token n’a été fourni." #~ msgid "User '%(name)s' has been deactivated" #~ msgstr "Le membre '%(name)s' a été désactivé" #~ msgid "Invite" #~ msgstr "Invitez" - From 0a9d16b40a715b96625bd29e9d1c82d77a352dd9 Mon Sep 17 00:00:00 2001 From: Adrien CLERC Date: Tue, 15 May 2018 22:05:42 +0200 Subject: [PATCH 17/21] compile translations to MO --- .../translations/fr/LC_MESSAGES/messages.mo | Bin 9762 -> 9912 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/ihatemoney/translations/fr/LC_MESSAGES/messages.mo b/ihatemoney/translations/fr/LC_MESSAGES/messages.mo index 47b801d4f61bafde6443423ad1f7325ec73e465d..c48d7cb8eb991df15c4e83242e6e115e1d40addd 100644 GIT binary patch delta 3121 zcma*neQZ^C9mnzCEilSu1zHMiDQ$n3_O@l?p)HV(LO>oTTA0DXBtF39-a|R=KJA=) ztAeb^k|idZjGf8Etg#tm=4NJd>6YP;WNY0(jbULkCQ8D#pcof*3qH)?)BAIJG||6I zn)^Brzu)is@cW%UIe2d8BON16#+YN(#`NLi*oi;J7=C~QcoXO1idn|Y#0@wNw__YX zhI4RFWCoXU@8fJ7!+H2!)cgxL17DkEM$CLJe!_#rcnxpEzhOO2WA!?0L>`$taXPL+ zaxw#`d0S8c4xkcwAi6(}wcMXX1w4aVuYy|d2l0`qi`RLez;9q1{yZA+7krHS7-iJp zDO`e2VLM*LMfe_S{%yA#(}gt{CW^c=1E@r{BTJf3;ZjVGaG}_VBh*6IB0oTNU>ez}LW}A|C-TblqguWdRrz4_d_QX3NR|sNa17PDZ=trNg39y) zDxpiL1>ZuQnRifIbPHAaJSHcw0iVRRsD!Sf)_WJV-k(w9V^rNpd{fPZT9&{D+<^*^ zLbdiNT0Dg}@Fm=Y-zNPszJuHFKH8%Bqu7ZrViK>S3ap}hb+QdtVK>erzWEFn$}oql zZN7q8Jc&9B|3Hha4AQub{HPP3L{)MCTX6_g@xzf{Lmje5BcDKR>C?CtpTYISH^1US zpV<=nsR}-e3S36IYrcVM-5FGXXR!reKn1uGy}yb|=msj#?;~#_O)wuu@8cXKorzi; zA+~AaLIIYe7Q72p;bzonzbAU`;fa_rUq&VP&xKQ!w2`%1ybtw%VHmgL5u|zMRaC&Y zBCn$2UT>iPI)xwdpc!XzA~s?YHNin*nH;L}hfsU?RqVk>aV1Wo#@|H#%q@PjZaoJ{ zTbji1b3}D?0K4#`P4r)_De^#pP9Q~@Z=#+*hYI{Es=|w?ui-kXWACF{ehby9Do(6A zn8dTV5;^eZXSfWn;~K1`T8+OusLW^|(doI!6V3cJYQ+1f-gOdInXE;X_Hk6H8PpzS zkw5b#ezf8#BpLI4T#j#|GK_IzR9Q8uC2gp6R<^JQD!rcv>dj76z)z!km_sG_Ad+h^WH{>*#P{VXQweFE2E51zm*YW^Rh_qSTv162~|NGYQR?7%kc#jR*@ z7!}}gq$u-Z^!y6m;Ql7=z+YOvVN51CK=>V0;x)8Kho&1laUZIq$49u(W}e1f_!O%0 zo2X+to&WS|StFLP8}GnT)TUlSCH5!e6EpM4UWcX|Ev`Uq!9Az~_o6!eIaHjHEEk$^ z3^(CJxEWu=tyrC$s$eH}bAK4s!jq^3M^T@}^Qh0^Wz<>vEo$5!qkEODMB=DKYLNuC znIu7Xe!SP3`pO4M-4<9U~yiBkmK78gvJQm!T zy*PM#_KCT+RJh;C+oD$(b~8?9eE;n`Vl#^=-~U3v8y~CL9UK4qoU^g-M5r#Trnc&V;srsw1e!_4WL|3p>IUCVpcEbm=o`+5gfuL|B+(A@W*Cw&7wJ$Lr> z_4LFCQ+{dry++OfzT(qzRRDwMsv8@%3Z z!=-ODk9GKkJh9zFhbAkYlP~#ZcdA%)vPvgiAk8qBbBp(d6Y5&_w(g#+xOunarn0V2 zu35+C3asE*?U54Y(iz7_)Z7TRkYi}~{PgaU4 zZ?eJ@WF_AYyWUWlA}3rueB&llLewYci>_Y^D_Lg!;IeH9m->yp zdxqVmEcz0i+J$|I{_w@g#8Bv86PrWVFX;?TFF87I;+cuDWOqK5bC&uOV-x3t+V*7l zvcJ76>lfUT&6l%TXN4K0flltA=h*+9V*kBuB15HWZG_0W U1<&y&#u6qS4e}??d+hPQ0RA*Yk^lez delta 2999 zcmZA2e{9tC9mny{m31vGU=``Ll(wJiud70DcP$OG78%HouoZ9}VNMP4%iWjqh2CAc zA5DefWo~HpOT;g8W=5xtMo%+!DU*dRbLKY3mMM}j7oFgmEo?E6{h`jy1-Iv`J+frU z@qK)Me7>L0`}6vIzU8M|CWjvAC@eO{9I7^^5szXoK8Ia+9s^i8- z8l*O}9@Tyes^h(=0uGk1zlB$G{RnEDAEDlxLiP8vnu0NmWlnQLzJ(h2&)ACZmG9S3 z{xPnv#f4ZzwL5`r_;YN>8C;5$3+A3%409bu6}}B=Vzwh)n>!1fT*t{iRJ7kiRs0mH z;R)1>B~<07a0#A4wfk$?f1#47r8>>D8*W!~@pT`Th0q-Cdy+4UvcoI8t1~tJdrmrMBu#fRg4<|~% zK~rn8=5M(?w;d0dO{%raF?Rbxb8WRh^q9L*o|lL0A5CI*9rdg^DUWUsNGUTYB4XMqCbtA*qf+?&!hUk zh-!ZsS7Tuw(_Y8PTD%Frfr`F_8u&aa!m7@>!WN^xgD`5Xqo_T!6V+}{`8tOx@bN4r!XE@QCzKH7J6l#F8s5Lu>D&!*S#U;wIZEM=*()-+$9m}N0y@Z=+ zoBC??j|2Xynru}nVG~ZyoqDFGBH+JT+s?mxrfyrXF);PQ!p{e)GP$|OA1*2e0+Ff5 z>Vg&i1J`7y)-QgjqQ9>v7VU}k+1}VdU+?m0Z!}tS%eJ0jcihW*=~T~p`W>*n(O7J@ z-AdcrH_#vRuc{9=e=+Z-a$d>~r|+_Zne<)z^X}(Hl8!eTiKmnP%KB3_T4UDcJNdrDCpBw7b*!Ov;P+pVWschQfX{*t{^@+0D%My7Am~*|2|Wuq9yqiQvgV*za%f zI6l!3ninxU{OQhiuHI|7(eH13(5gI}aP4j{Wj3ZWNr$QCxr*=c+>A|x{d0{6gJI&L z(%qhuntePwPR{!Km)_ZK2h8@_SiRJ^GfG3VIW_L(+yhs%2|v`-?$s5! znVYQdH?8z9G_`WCdfDT#x%p&m%C*@vgN!*@JL+a_vh-}y%|^_yGdAXqsYBXIWXImlkg{6VubQ+OH)A@vB=`2OSC+(J)RT>7~4EF5~1_On*n~AqIW3wT0tHtt$f&f0Lbx zg`IfZO%PWq>`u_wRrwM7rF4Feqa^HjI-fN+yXD&N2>Y?nrg>ZOP^gQ7o(ip^zjs3; z95=VE<9L5t7snHA2m4snl#_H>QhR0XsWa=ubKZEVXog&ybd$R>uKiy_e_eYgZ|`jn zv#{fyn Date: Sun, 17 Jun 2018 09:48:19 +0200 Subject: [PATCH 18/21] fix fr l10n --- ihatemoney/translations/fr/LC_MESSAGES/messages.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ihatemoney/translations/fr/LC_MESSAGES/messages.po b/ihatemoney/translations/fr/LC_MESSAGES/messages.po index 5179f605..a4a3e1b8 100644 --- a/ihatemoney/translations/fr/LC_MESSAGES/messages.po +++ b/ihatemoney/translations/fr/LC_MESSAGES/messages.po @@ -242,7 +242,7 @@ msgid "" "list until its balance becomes zero." msgstr "" "Le membre « %(name)s » a été désactivé. Il continuera d’apparaître jusqu'à " -"ce que sa balance soit nulle." +"ce que son solde soit nul." #: web.py:465 #, python-format From a5240fb8a331f50ffc986c25a4a2ddf17e8fd693 Mon Sep 17 00:00:00 2001 From: Adrien CLERC Date: Sun, 17 Jun 2018 09:49:10 +0200 Subject: [PATCH 19/21] compile l10n --- .../translations/fr/LC_MESSAGES/messages.mo | Bin 9912 -> 9909 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/ihatemoney/translations/fr/LC_MESSAGES/messages.mo b/ihatemoney/translations/fr/LC_MESSAGES/messages.mo index c48d7cb8eb991df15c4e83242e6e115e1d40addd..3fa8d8f4c52f34ffbdb3e90d8579f61864b1ce5a 100644 GIT binary patch delta 315 zcmXZWKMO%&6vy#X;u3CGM3K71z2r|8qlHXjVzU?+Zi$4#!Yl9s*o{&Wv+`QZ?8^6v z<@ucR>pZ7(Yh7BWzP6r-NH!&s!yOjzfk}L01dX(a8!a?3j~;BIhHVUD7Y%e!&9AVE z8?50S>*%&b;@GfcEm?{IfgiUR#vOX`h$XzD51**!UjP1s>JZKT9ipQ;Ac_IZ{PQx# t=qsrHcTnx`*`zv!Lr}y8s{U|Zdi6wTJTj-_VSm8u%*@ezc;2Uyt}g*bB=7(L delta 318 zcmXZWu}Z^G6vpub(k8XFC@GXy0)dzif^A1fsdNo)bt}XeP`JdwI20)vd;+&Fc>p&{ z5X4>ZSvuR@|D~CJ=X{)d`97ns=;O{PauEq!kq}Ef$6vH@hds2;L>d^NgJW#sH5xd_ zBV6DyD%AHYoZ>59;SbKx^hAajc~VHga8J<2H?*)s6E`@)Z*1Wf_1#~+i@z^&h`PuL z>H-7oU|8oD*k`{){r(2^|A|l9Wl#hod_ldxS4*>d+KZjrSjAbAI!})exsyFBl~!xl GKKKVbhb3MB From 26d1e038772798cc3a916643a0e71cf504a8fd3d Mon Sep 17 00:00:00 2001 From: Adrien CLERC Date: Tue, 19 Jun 2018 22:35:23 +0200 Subject: [PATCH 20/21] Add changelog entry --- CHANGELOG.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 81df6bbf..663a861b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,7 +6,7 @@ This document describes changes between each past release. 2.1.1 (unreleased) ------------------ -- Nothing changed yet. +- Regenerate translations (#338) 2.1 (2018-02-16) From f9cc4e56230ce04f58d457bfc8f468d56e53cb36 Mon Sep 17 00:00:00 2001 From: mduret Date: Mon, 16 Jul 2018 22:08:15 +0200 Subject: [PATCH 21/21] fix install with pip10 (#341) * fix the docker build with pip10 * maj tricks https://github.com/spiral-project/ihatemoney/pull/341 --- setup.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/setup.py b/setup.py index ec40c852..974a70f7 100644 --- a/setup.py +++ b/setup.py @@ -2,16 +2,6 @@ import codecs import os from setuptools import setup, find_packages -try: - from pip.req import parse_requirements - from pip.download import PipSession -except ImportError: - print('Cannot find pip.') - raise - -# Get requirements from the requirements.txt file. -pip_requirements = parse_requirements("requirements.txt", session=PipSession()) -install_requires = [str(ir.req) for ir in pip_requirements] here = os.path.abspath(os.path.dirname(__file__)) @@ -23,6 +13,13 @@ def read_file(filename): return content +def parse_requirements(filename): + """ load requirements from a pip requirements file """ + with open(filename) as lines: + lineiter = (line.strip() for line in lines) + return [line for line in lineiter if line and not line.startswith("#")] + + README = read_file('README.rst') CHANGELOG = read_file('CHANGELOG.rst') @@ -59,5 +56,5 @@ setup(name='ihatemoney', packages=find_packages(), include_package_data=True, zip_safe=False, - install_requires=install_requires, + install_requires=parse_requirements('requirements.txt'), entry_points=ENTRY_POINTS)
{{ _("Who?") }}{{ _("Paid") }}{{ _("Spent") }}{{ _("Balance") }}
{{ member.name }}{{ "%0.2f"|format(paid[member.id]) }}{{ "%0.2f"|format(spent[member.id]) }}{{ "%0.2f"|format(balance[member.id]) }}{{ stat.member.name }}{{ "%0.2f"|format(stat.paid) }}{{ "%0.2f"|format(stat.spent) }}{{ "%0.2f"|format(stat.balance) }}
alexis20.0031.67alexis20.0031.67-11.67fred20.005.83fred20.005.8314.17tata0.002.50tata0.002.50-2.50toto0.000.00toto0.000.000.00