Do not allow negative weights on users (Fixes #362) (#366)

This commit is contained in:
Alexis Metaireau 2019-01-03 13:29:56 +01:00 committed by GitHub
parent 04adfe4155
commit d55b996170
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 70 additions and 5 deletions

View file

@ -8,10 +8,16 @@ This document describes changes between each past release.
Added Added
===== =====
- Add CORS headers in the API (#407) - Add CORS headers in the API (#407)
- Document database migrations (#390) - Document database migrations (#390)
- Allow basic math operations in amount field (#413) - Allow basic math operations in amount field (#413)
Fixed
=====
- Do not allow negative weights on users (#366)
3.0 (2018-11-25) 3.0 (2018-11-25)
---------------- ----------------

View file

@ -2,7 +2,7 @@ from flask_wtf.form import FlaskForm
from wtforms.fields.core import SelectField, SelectMultipleField from wtforms.fields.core import SelectField, SelectMultipleField
from wtforms.fields.html5 import DateField, DecimalField from wtforms.fields.html5 import DateField, DecimalField
from wtforms.fields.simple import PasswordField, SubmitField, TextAreaField, StringField from wtforms.fields.simple import PasswordField, SubmitField, TextAreaField, StringField
from wtforms.validators import Email, Required, ValidationError, EqualTo from wtforms.validators import Email, Required, ValidationError, EqualTo, NumberRange
from flask_babel import lazy_gettext as _ from flask_babel import lazy_gettext as _
from flask import request from flask import request
from werkzeug.security import generate_password_hash from werkzeug.security import generate_password_hash
@ -174,9 +174,11 @@ class BillForm(FlaskForm):
class MemberForm(FlaskForm): class MemberForm(FlaskForm):
name = StringField(_("Name"), validators=[Required()]) name = StringField(_("Name"), validators=[Required()])
weight = CommaDecimalField(_("Weight"), default=1)
weight_validators = [NumberRange(min=0.1, message=_("Weights should be positive"))]
weight = CommaDecimalField(_("Weight"), default=1,
validators=weight_validators)
submit = SubmitField(_("Add")) submit = SubmitField(_("Add"))
def __init__(self, project, edit=False, *args, **kwargs): def __init__(self, project, edit=False, *args, **kwargs):

View file

@ -79,6 +79,9 @@ msgstr ""
msgid "Weight" msgid "Weight"
msgstr "" msgstr ""
msgid "Weights should be positive"
msgstr ""
msgid "Add" msgid "Add"
msgstr "" msgstr ""

View file

@ -0,0 +1,38 @@
"""Migrate negative weights
Revision ID: a67119aa3ee5
Revises: afbf27e6ef20
Create Date: 2018-12-25 18:34:20.220844
"""
# revision identifiers, used by Alembic.
revision = 'a67119aa3ee5'
down_revision = 'afbf27e6ef20'
from alembic import op
import sqlalchemy as sa
# Snapshot of the person table
person_helper = sa.Table(
'person', sa.MetaData(),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('project_id', sa.String(length=64), nullable=True),
sa.Column('name', sa.UnicodeText(), nullable=True),
sa.Column('activated', sa.Boolean(), nullable=True),
sa.Column('weight', sa.Float(), nullable=True),
sa.ForeignKeyConstraint(['project_id'], ['project.id'], ),
sa.PrimaryKeyConstraint('id')
)
def upgrade():
op.execute(
person_helper.update()
.where(person_helper.c.weight <= 0)
.values(weight=1)
)
def downgrade():
# Downgrade path is not possible, because information has been lost.
pass

View file

@ -376,8 +376,9 @@ class Bill(db.Model):
def pay_each(self): def pay_each(self):
"""Compute what each share has to pay""" """Compute what each share has to pay"""
if self.owers: if self.owers:
# FIXME: SQL might dot that more efficiently # FIXME: SQL might do that more efficiently
return self.amount / sum(i.weight for i in self.owers) weights = sum(i.weight for i in self.owers)
return self.amount / weights
else: else:
return 0 return 0

View file

@ -621,6 +621,18 @@ class BudgetTestCase(IhatemoneyTestCase):
resp = self.client.get("/raclette/") resp = self.client.get("/raclette/")
self.assertNotIn('extra-info', resp.data.decode('utf-8')) self.assertNotIn('extra-info', resp.data.decode('utf-8'))
def test_negative_weight(self):
self.post_project("raclette")
# Add one user and edit it to have a negative share
self.client.post("/raclette/members/add", data={'name': 'alexis'})
resp = self.client.post("/raclette/members/1/edit", data={'name': 'alexis', 'weight': -1})
# An error should be generated, and its weight should still be 1.
self.assertIn('<p class="alert alert-danger">', resp.data.decode('utf-8'))
self.assertEqual(len(models.Project.query.get('raclette').members), 1)
self.assertEqual(models.Project.query.get('raclette').members[0].weight, 1)
def test_rounding(self): def test_rounding(self):
self.post_project("raclette") self.post_project("raclette")

View file

@ -105,6 +105,9 @@ msgstr "Nom"
msgid "Weight" msgid "Weight"
msgstr "Parts" msgstr "Parts"
msgid "Weights should be positive"
msgstr "Les parts doivent être positives"
msgid "Add" msgid "Add"
msgstr "Ajouter" msgstr "Ajouter"