mirror of
https://github.com/spiral-project/ihatemoney.git
synced 2025-04-30 18:22:38 +02:00
Make ihatemoney Py2/3 compatible
Flask-wtf>=0.13 is now required and Form is replaced by FlaskForm Py2/3 compatibility is assured by six
This commit is contained in:
parent
10a16a3b5c
commit
59a050e020
4 changed files with 122 additions and 108 deletions
|
@ -1,6 +1,8 @@
|
||||||
from flask_wtf import DateField, DecimalField, Email, Form, PasswordField, \
|
from flask_wtf.form import FlaskForm
|
||||||
Required, SelectField, SelectMultipleField, SubmitField, TextAreaField, \
|
from wtforms.fields.core import SelectField, SelectMultipleField
|
||||||
TextField, ValidationError
|
from wtforms.fields.html5 import DateField, DecimalField
|
||||||
|
from wtforms.fields.simple import PasswordField, SubmitField, TextAreaField, TextField
|
||||||
|
from wtforms.validators import Email, Required, ValidationError
|
||||||
from flask_babel import lazy_gettext as _
|
from flask_babel import lazy_gettext as _
|
||||||
from flask import request
|
from flask import request
|
||||||
|
|
||||||
|
@ -35,7 +37,7 @@ class CommaDecimalField(DecimalField):
|
||||||
return super(CommaDecimalField, self).process_formdata(value)
|
return super(CommaDecimalField, self).process_formdata(value)
|
||||||
|
|
||||||
|
|
||||||
class EditProjectForm(Form):
|
class EditProjectForm(FlaskForm):
|
||||||
name = TextField(_("Project name"), validators=[Required()])
|
name = TextField(_("Project name"), validators=[Required()])
|
||||||
password = TextField(_("Private code"), validators=[Required()])
|
password = TextField(_("Private code"), validators=[Required()])
|
||||||
contact_email = TextField(_("Email"), validators=[Required(), Email()])
|
contact_email = TextField(_("Email"), validators=[Required(), Email()])
|
||||||
|
@ -75,13 +77,13 @@ class ProjectForm(EditProjectForm):
|
||||||
"that you will be able to remember.")))
|
"that you will be able to remember.")))
|
||||||
|
|
||||||
|
|
||||||
class AuthenticationForm(Form):
|
class AuthenticationForm(FlaskForm):
|
||||||
id = TextField(_("Project identifier"), validators=[Required()])
|
id = TextField(_("Project identifier"), validators=[Required()])
|
||||||
password = PasswordField(_("Private code"), validators=[Required()])
|
password = PasswordField(_("Private code"), validators=[Required()])
|
||||||
submit = SubmitField(_("Get in"))
|
submit = SubmitField(_("Get in"))
|
||||||
|
|
||||||
|
|
||||||
class PasswordReminder(Form):
|
class PasswordReminder(FlaskForm):
|
||||||
id = TextField(_("Project identifier"), validators=[Required()])
|
id = TextField(_("Project identifier"), validators=[Required()])
|
||||||
submit = SubmitField(_("Send me the code by email"))
|
submit = SubmitField(_("Send me the code by email"))
|
||||||
|
|
||||||
|
@ -90,7 +92,7 @@ class PasswordReminder(Form):
|
||||||
raise ValidationError(_("This project does not exists"))
|
raise ValidationError(_("This project does not exists"))
|
||||||
|
|
||||||
|
|
||||||
class BillForm(Form):
|
class BillForm(FlaskForm):
|
||||||
date = DateField(_("Date"), validators=[Required()], default=datetime.now)
|
date = DateField(_("Date"), validators=[Required()], default=datetime.now)
|
||||||
what = TextField(_("What?"), validators=[Required()])
|
what = TextField(_("What?"), validators=[Required()])
|
||||||
payer = SelectField(_("Payer"), validators=[Required()], coerce=int)
|
payer = SelectField(_("Payer"), validators=[Required()], coerce=int)
|
||||||
|
@ -125,7 +127,7 @@ class BillForm(Form):
|
||||||
raise ValidationError(_("Bills can't be null"))
|
raise ValidationError(_("Bills can't be null"))
|
||||||
|
|
||||||
|
|
||||||
class MemberForm(Form):
|
class MemberForm(FlaskForm):
|
||||||
|
|
||||||
name = TextField(_("Name"), validators=[Required()])
|
name = TextField(_("Name"), validators=[Required()])
|
||||||
weight = CommaDecimalField(_("Weight"), default=1)
|
weight = CommaDecimalField(_("Weight"), default=1)
|
||||||
|
@ -158,7 +160,7 @@ class MemberForm(Form):
|
||||||
self.weight.data = member.weight
|
self.weight.data = member.weight
|
||||||
|
|
||||||
|
|
||||||
class InviteForm(Form):
|
class InviteForm(FlaskForm):
|
||||||
emails = TextAreaField(_("People to notify"))
|
emails = TextAreaField(_("People to notify"))
|
||||||
submit = SubmitField(_("Send invites"))
|
submit = SubmitField(_("Send invites"))
|
||||||
|
|
||||||
|
@ -170,13 +172,13 @@ class InviteForm(Form):
|
||||||
email=email))
|
email=email))
|
||||||
|
|
||||||
|
|
||||||
class CreateArchiveForm(Form):
|
class CreateArchiveForm(FlaskForm):
|
||||||
name = TextField(_("Name for this archive (optional)"), validators=[])
|
name = TextField(_("Name for this archive (optional)"), validators=[])
|
||||||
start_date = DateField(_("Start date"), validators=[Required()])
|
start_date = DateField(_("Start date"), validators=[Required()])
|
||||||
end_date = DateField(_("End date"), validators=[Required()], default=datetime.now)
|
end_date = DateField(_("End date"), validators=[Required()], default=datetime.now)
|
||||||
|
|
||||||
|
|
||||||
class ExportForm(Form):
|
class ExportForm(FlaskForm):
|
||||||
export_type = SelectField(_("What do you want to download ?"),
|
export_type = SelectField(_("What do you want to download ?"),
|
||||||
validators=[Required()],
|
validators=[Required()],
|
||||||
coerce=str,
|
coerce=str,
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
flask>=0.9
|
flask>=0.11
|
||||||
flask-wtf==0.8
|
flask-wtf>=0.13
|
||||||
flask-sqlalchemy
|
flask-sqlalchemy
|
||||||
flask-mail>=0.8
|
flask-mail>=0.8
|
||||||
Flask-Migrate==1.8.0
|
Flask-Migrate>=1.8.0
|
||||||
flask-babel
|
flask-babel
|
||||||
flask-rest
|
flask-rest
|
||||||
jinja2==2.6
|
jinja2>=2.6
|
||||||
raven
|
raven
|
||||||
blinker
|
blinker
|
||||||
|
six>=1.10
|
||||||
|
|
151
budget/tests.py
151
budget/tests.py
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
try:
|
try:
|
||||||
import unittest2 as unittest
|
import unittest2 as unittest
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -8,6 +9,7 @@ import base64
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
import six
|
||||||
|
|
||||||
os.environ['FLASK_SETTINGS_MODULE'] = 'default_settings'
|
os.environ['FLASK_SETTINGS_MODULE'] = 'default_settings'
|
||||||
|
|
||||||
|
@ -23,7 +25,7 @@ class TestCase(unittest.TestCase):
|
||||||
run.app.config['TESTING'] = True
|
run.app.config['TESTING'] = True
|
||||||
|
|
||||||
run.app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:///memory"
|
run.app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:///memory"
|
||||||
run.app.config['CSRF_ENABLED'] = False # simplify the tests
|
run.app.config['WTF_CSRF_ENABLED'] = False # simplify the tests
|
||||||
self.app = run.app.test_client()
|
self.app = run.app.test_client()
|
||||||
try:
|
try:
|
||||||
models.db.init_app(run.app)
|
models.db.init_app(run.app)
|
||||||
|
@ -56,7 +58,7 @@ class TestCase(unittest.TestCase):
|
||||||
})
|
})
|
||||||
|
|
||||||
def create_project(self, name):
|
def create_project(self, name):
|
||||||
models.db.session.add(models.Project(id=name, name=unicode(name),
|
models.db.session.add(models.Project(id=name, name=six.text_type(name),
|
||||||
password=name, contact_email="%s@notmyidea.org" % name))
|
password=name, contact_email="%s@notmyidea.org" % name))
|
||||||
models.db.session.commit()
|
models.db.session.commit()
|
||||||
|
|
||||||
|
@ -96,7 +98,7 @@ class BudgetTestCase(TestCase):
|
||||||
response = self.app.post("/raclette/invite",
|
response = self.app.post("/raclette/invite",
|
||||||
data={"emails": "toto"})
|
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.decode('utf-8'))
|
||||||
|
|
||||||
# mixing good and wrong adresses shouldn't send any messages
|
# mixing good and wrong adresses shouldn't send any messages
|
||||||
with run.mail.record_messages() as outbox:
|
with run.mail.record_messages() as outbox:
|
||||||
|
@ -192,7 +194,7 @@ class BudgetTestCase(TestCase):
|
||||||
|
|
||||||
# check fred is present in the bills page
|
# check fred is present in the bills page
|
||||||
result = self.app.get("/raclette/")
|
result = self.app.get("/raclette/")
|
||||||
self.assertIn("fred", result.data)
|
self.assertIn("fred", result.data.decode('utf-8'))
|
||||||
|
|
||||||
# remove fred
|
# remove fred
|
||||||
self.app.post("/raclette/members/%s/delete" %
|
self.app.post("/raclette/members/%s/delete" %
|
||||||
|
@ -208,7 +210,7 @@ class BudgetTestCase(TestCase):
|
||||||
# bound him to a bill
|
# bound him to a bill
|
||||||
result = self.app.post("/raclette/add", data={
|
result = self.app.post("/raclette/add", data={
|
||||||
'date': '2011-08-10',
|
'date': '2011-08-10',
|
||||||
'what': u'fromage à raclette',
|
'what': 'fromage à raclette',
|
||||||
'payer': fred_id,
|
'payer': fred_id,
|
||||||
'payed_for': [fred_id, ],
|
'payed_for': [fred_id, ],
|
||||||
'amount': '25',
|
'amount': '25',
|
||||||
|
@ -225,10 +227,10 @@ class BudgetTestCase(TestCase):
|
||||||
# as fred is now deactivated, check that he is not listed when adding
|
# as fred is now deactivated, check that he is not listed when adding
|
||||||
# a bill or displaying the balance
|
# a bill or displaying the balance
|
||||||
result = self.app.get("/raclette/")
|
result = self.app.get("/raclette/")
|
||||||
self.assertNotIn("/raclette/members/%s/delete" % fred_id, result.data)
|
self.assertNotIn(("/raclette/members/%s/delete" % fred_id), result.data.decode('utf-8'))
|
||||||
|
|
||||||
result = self.app.get("/raclette/add")
|
result = self.app.get("/raclette/add")
|
||||||
self.assertNotIn("fred", result.data)
|
self.assertNotIn("fred", result.data.decode('utf-8'))
|
||||||
|
|
||||||
# adding him again should reactivate him
|
# adding him again should reactivate him
|
||||||
self.app.post("/raclette/members/add", data={'name': 'fred'})
|
self.app.post("/raclette/members/add", data={'name': 'fred'})
|
||||||
|
@ -257,7 +259,7 @@ class BudgetTestCase(TestCase):
|
||||||
# bound him to a bill
|
# bound him to a bill
|
||||||
self.app.post("/raclette/add", data={
|
self.app.post("/raclette/add", data={
|
||||||
'date': '2011-08-10',
|
'date': '2011-08-10',
|
||||||
'what': u'fromage à raclette',
|
'what': 'fromage à raclette',
|
||||||
'payer': alexis.id,
|
'payer': alexis.id,
|
||||||
'payed_for': [alexis.id, ],
|
'payed_for': [alexis.id, ],
|
||||||
'amount': '25',
|
'amount': '25',
|
||||||
|
@ -295,7 +297,7 @@ class BudgetTestCase(TestCase):
|
||||||
# try to authenticate without credentials should redirect
|
# try to authenticate without credentials should redirect
|
||||||
# to the authentication page
|
# to the authentication page
|
||||||
resp = self.app.post("/authenticate")
|
resp = self.app.post("/authenticate")
|
||||||
self.assertIn("Authentication", resp.data)
|
self.assertIn("Authentication", resp.data.decode('utf-8'))
|
||||||
|
|
||||||
# raclette that the login / logout process works
|
# raclette that the login / logout process works
|
||||||
self.create_project("raclette")
|
self.create_project("raclette")
|
||||||
|
@ -303,14 +305,14 @@ class BudgetTestCase(TestCase):
|
||||||
# try to see the project while not being authenticated should redirect
|
# try to see the project while not being authenticated should redirect
|
||||||
# to the authentication page
|
# to the authentication page
|
||||||
resp = self.app.get("/raclette", follow_redirects=True)
|
resp = self.app.get("/raclette", follow_redirects=True)
|
||||||
self.assertIn("Authentication", resp.data)
|
self.assertIn("Authentication", resp.data.decode('utf-8'))
|
||||||
|
|
||||||
# try to connect with wrong credentials should not work
|
# try to connect with wrong credentials should not work
|
||||||
with run.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'})
|
||||||
|
|
||||||
self.assertIn("Authentication", resp.data)
|
self.assertIn("Authentication", resp.data.decode('utf-8'))
|
||||||
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
|
||||||
|
@ -318,7 +320,7 @@ class BudgetTestCase(TestCase):
|
||||||
resp = c.post("/authenticate",
|
resp = c.post("/authenticate",
|
||||||
data={'id': 'raclette', 'password': 'raclette'})
|
data={'id': 'raclette', 'password': 'raclette'})
|
||||||
|
|
||||||
self.assertNotIn("Authentication", resp.data)
|
self.assertNotIn("Authentication", resp.data.decode('utf-8'))
|
||||||
self.assertIn('raclette', session)
|
self.assertIn('raclette', session)
|
||||||
self.assertEqual(session['raclette'], 'raclette')
|
self.assertEqual(session['raclette'], 'raclette')
|
||||||
|
|
||||||
|
@ -339,7 +341,7 @@ class BudgetTestCase(TestCase):
|
||||||
# create a bill
|
# create a bill
|
||||||
self.app.post("/raclette/add", data={
|
self.app.post("/raclette/add", data={
|
||||||
'date': '2011-08-10',
|
'date': '2011-08-10',
|
||||||
'what': u'fromage à raclette',
|
'what': 'fromage à raclette',
|
||||||
'payer': members_ids[0],
|
'payer': members_ids[0],
|
||||||
'payed_for': members_ids,
|
'payed_for': members_ids,
|
||||||
'amount': '25',
|
'amount': '25',
|
||||||
|
@ -351,7 +353,7 @@ class BudgetTestCase(TestCase):
|
||||||
# edit the bill
|
# edit the bill
|
||||||
self.app.post("/raclette/edit/%s" % bill.id, data={
|
self.app.post("/raclette/edit/%s" % bill.id, data={
|
||||||
'date': '2011-08-10',
|
'date': '2011-08-10',
|
||||||
'what': u'fromage à raclette',
|
'what': 'fromage à raclette',
|
||||||
'payer': members_ids[0],
|
'payer': members_ids[0],
|
||||||
'payed_for': members_ids,
|
'payed_for': members_ids,
|
||||||
'amount': '10',
|
'amount': '10',
|
||||||
|
@ -367,7 +369,7 @@ class BudgetTestCase(TestCase):
|
||||||
# test balance
|
# test balance
|
||||||
self.app.post("/raclette/add", data={
|
self.app.post("/raclette/add", data={
|
||||||
'date': '2011-08-10',
|
'date': '2011-08-10',
|
||||||
'what': u'fromage à raclette',
|
'what': 'fromage à raclette',
|
||||||
'payer': members_ids[0],
|
'payer': members_ids[0],
|
||||||
'payed_for': members_ids,
|
'payed_for': members_ids,
|
||||||
'amount': '19',
|
'amount': '19',
|
||||||
|
@ -375,7 +377,7 @@ class BudgetTestCase(TestCase):
|
||||||
|
|
||||||
self.app.post("/raclette/add", data={
|
self.app.post("/raclette/add", data={
|
||||||
'date': '2011-08-10',
|
'date': '2011-08-10',
|
||||||
'what': u'fromage à raclette',
|
'what': 'fromage à raclette',
|
||||||
'payer': members_ids[1],
|
'payer': members_ids[1],
|
||||||
'payed_for': members_ids[0],
|
'payed_for': members_ids[0],
|
||||||
'amount': '20',
|
'amount': '20',
|
||||||
|
@ -383,7 +385,7 @@ class BudgetTestCase(TestCase):
|
||||||
|
|
||||||
self.app.post("/raclette/add", data={
|
self.app.post("/raclette/add", data={
|
||||||
'date': '2011-08-10',
|
'date': '2011-08-10',
|
||||||
'what': u'fromage à raclette',
|
'what': 'fromage à raclette',
|
||||||
'payer': members_ids[1],
|
'payer': members_ids[1],
|
||||||
'payed_for': members_ids,
|
'payed_for': members_ids,
|
||||||
'amount': '17',
|
'amount': '17',
|
||||||
|
@ -395,7 +397,7 @@ class BudgetTestCase(TestCase):
|
||||||
#Bill with negative amount
|
#Bill with negative amount
|
||||||
self.app.post("/raclette/add", data={
|
self.app.post("/raclette/add", data={
|
||||||
'date': '2011-08-12',
|
'date': '2011-08-12',
|
||||||
'what': u'fromage à raclette',
|
'what': 'fromage à raclette',
|
||||||
'payer': members_ids[0],
|
'payer': members_ids[0],
|
||||||
'payed_for': members_ids,
|
'payed_for': members_ids,
|
||||||
'amount': '-25'
|
'amount': '-25'
|
||||||
|
@ -406,7 +408,7 @@ class BudgetTestCase(TestCase):
|
||||||
#add a bill with a comma
|
#add a bill with a comma
|
||||||
self.app.post("/raclette/add", data={
|
self.app.post("/raclette/add", data={
|
||||||
'date': '2011-08-01',
|
'date': '2011-08-01',
|
||||||
'what': u'fromage à raclette',
|
'what': 'fromage à raclette',
|
||||||
'payer': members_ids[0],
|
'payer': members_ids[0],
|
||||||
'payed_for': members_ids,
|
'payed_for': members_ids,
|
||||||
'amount': '25,02',
|
'amount': '25,02',
|
||||||
|
@ -427,7 +429,7 @@ class BudgetTestCase(TestCase):
|
||||||
# test balance
|
# test balance
|
||||||
self.app.post("/raclette/add", data={
|
self.app.post("/raclette/add", data={
|
||||||
'date': '2011-08-10',
|
'date': '2011-08-10',
|
||||||
'what': u'fromage à raclette',
|
'what': 'fromage à raclette',
|
||||||
'payer': members_ids[0],
|
'payer': members_ids[0],
|
||||||
'payed_for': members_ids,
|
'payed_for': members_ids,
|
||||||
'amount': '10',
|
'amount': '10',
|
||||||
|
@ -435,7 +437,7 @@ class BudgetTestCase(TestCase):
|
||||||
|
|
||||||
self.app.post("/raclette/add", data={
|
self.app.post("/raclette/add", data={
|
||||||
'date': '2011-08-10',
|
'date': '2011-08-10',
|
||||||
'what': u'pommes de terre',
|
'what': 'pommes de terre',
|
||||||
'payer': members_ids[1],
|
'payer': members_ids[1],
|
||||||
'payed_for': members_ids,
|
'payed_for': members_ids,
|
||||||
'amount': '10',
|
'amount': '10',
|
||||||
|
@ -452,12 +454,12 @@ class BudgetTestCase(TestCase):
|
||||||
self.app.post("/raclette/members/add", data={'name': 'tata', 'weight': 1})
|
self.app.post("/raclette/members/add", data={'name': 'tata', 'weight': 1})
|
||||||
|
|
||||||
resp = self.app.get("/raclette/")
|
resp = self.app.get("/raclette/")
|
||||||
self.assertIn('extra-info', resp.data)
|
self.assertIn('extra-info', resp.data.decode('utf-8'))
|
||||||
|
|
||||||
self.app.post("/raclette/members/add", data={'name': 'freddy familly', 'weight': 4})
|
self.app.post("/raclette/members/add", data={'name': 'freddy familly', 'weight': 4})
|
||||||
|
|
||||||
resp = self.app.get("/raclette/")
|
resp = self.app.get("/raclette/")
|
||||||
self.assertNotIn('extra-info', resp.data)
|
self.assertNotIn('extra-info', resp.data.decode('utf-8'))
|
||||||
|
|
||||||
|
|
||||||
def test_rounding(self):
|
def test_rounding(self):
|
||||||
|
@ -471,7 +473,7 @@ class BudgetTestCase(TestCase):
|
||||||
# create bills
|
# create bills
|
||||||
self.app.post("/raclette/add", data={
|
self.app.post("/raclette/add", data={
|
||||||
'date': '2011-08-10',
|
'date': '2011-08-10',
|
||||||
'what': u'fromage à raclette',
|
'what': 'fromage à raclette',
|
||||||
'payer': 1,
|
'payer': 1,
|
||||||
'payed_for': [1, 2, 3],
|
'payed_for': [1, 2, 3],
|
||||||
'amount': '24.36',
|
'amount': '24.36',
|
||||||
|
@ -479,7 +481,7 @@ class BudgetTestCase(TestCase):
|
||||||
|
|
||||||
self.app.post("/raclette/add", data={
|
self.app.post("/raclette/add", data={
|
||||||
'date': '2011-08-10',
|
'date': '2011-08-10',
|
||||||
'what': u'red wine',
|
'what': 'red wine',
|
||||||
'payer': 2,
|
'payer': 2,
|
||||||
'payed_for': [1],
|
'payed_for': [1],
|
||||||
'amount': '19.12',
|
'amount': '19.12',
|
||||||
|
@ -487,7 +489,7 @@ class BudgetTestCase(TestCase):
|
||||||
|
|
||||||
self.app.post("/raclette/add", data={
|
self.app.post("/raclette/add", data={
|
||||||
'date': '2011-08-10',
|
'date': '2011-08-10',
|
||||||
'what': u'delicatessen',
|
'what': 'delicatessen',
|
||||||
'payer': 1,
|
'payer': 1,
|
||||||
'payed_for': [1, 2],
|
'payed_for': [1, 2],
|
||||||
'amount': '22',
|
'amount': '22',
|
||||||
|
@ -500,7 +502,7 @@ class BudgetTestCase(TestCase):
|
||||||
result[models.Project.query.get("raclette").members[2].id] = -8.12
|
result[models.Project.query.get("raclette").members[2].id] = -8.12
|
||||||
# Since we're using floating point to store currency, we can have some rounding issues that prevent test from working.
|
# Since we're using floating point to store currency, we can have some rounding issues that prevent test from working.
|
||||||
# However, we should obtain the same values as the theorical ones if we round to 2 decimals, like in the UI.
|
# However, we should obtain the same values as the theorical ones if we round to 2 decimals, like in the UI.
|
||||||
for key, value in balance.iteritems():
|
for key, value in six.iteritems(balance):
|
||||||
self.assertEqual(round(value, 2), result[key])
|
self.assertEqual(round(value, 2), result[key])
|
||||||
|
|
||||||
def test_edit_project(self):
|
def test_edit_project(self):
|
||||||
|
@ -526,7 +528,7 @@ class BudgetTestCase(TestCase):
|
||||||
|
|
||||||
resp = self.app.post("/raclette/edit", data=new_data,
|
resp = self.app.post("/raclette/edit", data=new_data,
|
||||||
follow_redirects=True)
|
follow_redirects=True)
|
||||||
self.assertIn("Invalid email address", resp.data)
|
self.assertIn("Invalid email address", resp.data.decode('utf-8'))
|
||||||
|
|
||||||
def test_dashboard(self):
|
def test_dashboard(self):
|
||||||
response = self.app.get("/dashboard")
|
response = self.app.get("/dashboard")
|
||||||
|
@ -550,7 +552,7 @@ class BudgetTestCase(TestCase):
|
||||||
# create bills
|
# create bills
|
||||||
self.app.post("/raclette/add", data={
|
self.app.post("/raclette/add", data={
|
||||||
'date': '2011-08-10',
|
'date': '2011-08-10',
|
||||||
'what': u'fromage à raclette',
|
'what': 'fromage à raclette',
|
||||||
'payer': 1,
|
'payer': 1,
|
||||||
'payed_for': [1, 2, 3],
|
'payed_for': [1, 2, 3],
|
||||||
'amount': '10.0',
|
'amount': '10.0',
|
||||||
|
@ -558,7 +560,7 @@ class BudgetTestCase(TestCase):
|
||||||
|
|
||||||
self.app.post("/raclette/add", data={
|
self.app.post("/raclette/add", data={
|
||||||
'date': '2011-08-10',
|
'date': '2011-08-10',
|
||||||
'what': u'red wine',
|
'what': 'red wine',
|
||||||
'payer': 2,
|
'payer': 2,
|
||||||
'payed_for': [1],
|
'payed_for': [1],
|
||||||
'amount': '20',
|
'amount': '20',
|
||||||
|
@ -566,7 +568,7 @@ class BudgetTestCase(TestCase):
|
||||||
|
|
||||||
self.app.post("/raclette/add", data={
|
self.app.post("/raclette/add", data={
|
||||||
'date': '2011-08-10',
|
'date': '2011-08-10',
|
||||||
'what': u'delicatessen',
|
'what': 'delicatessen',
|
||||||
'payer': 1,
|
'payer': 1,
|
||||||
'payed_for': [1, 2],
|
'payed_for': [1, 2],
|
||||||
'amount': '10',
|
'amount': '10',
|
||||||
|
@ -594,7 +596,7 @@ class BudgetTestCase(TestCase):
|
||||||
# create bills
|
# create bills
|
||||||
self.app.post("/raclette/add", data={
|
self.app.post("/raclette/add", data={
|
||||||
'date': '2016-12-31',
|
'date': '2016-12-31',
|
||||||
'what': u'fromage à raclette',
|
'what': 'fromage à raclette',
|
||||||
'payer': 1,
|
'payer': 1,
|
||||||
'payed_for': [1, 2, 3],
|
'payed_for': [1, 2, 3],
|
||||||
'amount': '10.0',
|
'amount': '10.0',
|
||||||
|
@ -602,7 +604,7 @@ class BudgetTestCase(TestCase):
|
||||||
|
|
||||||
self.app.post("/raclette/add", data={
|
self.app.post("/raclette/add", data={
|
||||||
'date': '2016-12-31',
|
'date': '2016-12-31',
|
||||||
'what': u'red wine',
|
'what': 'red wine',
|
||||||
'payer': 2,
|
'payer': 2,
|
||||||
'payed_for': [1, 3],
|
'payed_for': [1, 3],
|
||||||
'amount': '20',
|
'amount': '20',
|
||||||
|
@ -610,7 +612,7 @@ class BudgetTestCase(TestCase):
|
||||||
|
|
||||||
self.app.post("/raclette/add", data={
|
self.app.post("/raclette/add", data={
|
||||||
'date': '2017-01-01',
|
'date': '2017-01-01',
|
||||||
'what': u'refund',
|
'what': 'refund',
|
||||||
'payer': 3,
|
'payer': 3,
|
||||||
'payed_for': [2],
|
'payed_for': [2],
|
||||||
'amount': '13.33',
|
'amount': '13.33',
|
||||||
|
@ -636,7 +638,7 @@ class BudgetTestCase(TestCase):
|
||||||
# create bills
|
# create bills
|
||||||
self.app.post("/raclette/add", data={
|
self.app.post("/raclette/add", data={
|
||||||
'date': '2016-12-31',
|
'date': '2016-12-31',
|
||||||
'what': u'fromage à raclette',
|
'what': 'fromage à raclette',
|
||||||
'payer': 1,
|
'payer': 1,
|
||||||
'payed_for': [1, 2, 3, 4],
|
'payed_for': [1, 2, 3, 4],
|
||||||
'amount': '10.0',
|
'amount': '10.0',
|
||||||
|
@ -644,7 +646,7 @@ class BudgetTestCase(TestCase):
|
||||||
|
|
||||||
self.app.post("/raclette/add", data={
|
self.app.post("/raclette/add", data={
|
||||||
'date': '2016-12-31',
|
'date': '2016-12-31',
|
||||||
'what': u'red wine',
|
'what': 'red wine',
|
||||||
'payer': 2,
|
'payer': 2,
|
||||||
'payed_for': [1, 3],
|
'payed_for': [1, 3],
|
||||||
'amount': '200',
|
'amount': '200',
|
||||||
|
@ -652,7 +654,7 @@ class BudgetTestCase(TestCase):
|
||||||
|
|
||||||
self.app.post("/raclette/add", data={
|
self.app.post("/raclette/add", data={
|
||||||
'date': '2017-01-01',
|
'date': '2017-01-01',
|
||||||
'what': u'refund',
|
'what': 'refund',
|
||||||
'payer': 3,
|
'payer': 3,
|
||||||
'payed_for': [2],
|
'payed_for': [2],
|
||||||
'amount': '13.33',
|
'amount': '13.33',
|
||||||
|
@ -663,13 +665,13 @@ class BudgetTestCase(TestCase):
|
||||||
'export_format': 'json',
|
'export_format': 'json',
|
||||||
'export_type': 'bills'
|
'export_type': 'bills'
|
||||||
})
|
})
|
||||||
expected = [{u'date': u'2017-01-01', u'what': u'refund',
|
expected = [{'date': '2017-01-01', 'what': 'refund',
|
||||||
u'amount': 13.33, u'payer_name': u'tata', u'payer_weight': 1.0, u'owers': [u'fred']},
|
'amount': 13.33, 'payer_name': 'tata', 'payer_weight': 1.0, 'owers': ['fred']},
|
||||||
{u'date': u'2016-12-31', u'what': u'red wine',
|
{'date': '2016-12-31', 'what': 'red wine',
|
||||||
u'amount': 200.0, u'payer_name': u'fred', u'payer_weight': 1.0, u'owers': [u'alexis', u'tata']},
|
'amount': 200.0, 'payer_name': 'fred', 'payer_weight': 1.0, 'owers': ['alexis', 'tata']},
|
||||||
{u'date': u'2016-12-31', u'what': u'fromage \xe0 raclette',
|
{'date': '2016-12-31', 'what': 'fromage \xe0 raclette',
|
||||||
u'amount': 10.0, u'payer_name': u'alexis', u'payer_weight': 2.0, u'owers': [u'alexis', u'fred', u'tata', u'p\xe9p\xe9']}]
|
'amount': 10.0, 'payer_name': 'alexis', 'payer_weight': 2.0, 'owers': ['alexis', 'fred', 'tata', 'p\xe9p\xe9']}]
|
||||||
self.assertEqual(json.loads(resp.data), expected)
|
self.assertEqual(json.loads(resp.data.decode('utf-8')), expected)
|
||||||
|
|
||||||
# generate csv export of bills
|
# generate csv export of bills
|
||||||
resp = self.app.post("/raclette/edit", data={
|
resp = self.app.post("/raclette/edit", data={
|
||||||
|
@ -680,7 +682,7 @@ class BudgetTestCase(TestCase):
|
||||||
"2017-01-01,refund,13.33,tata,1.0,fred",
|
"2017-01-01,refund,13.33,tata,1.0,fred",
|
||||||
"2016-12-31,red wine,200.0,fred,1.0,\"alexis, tata\"",
|
"2016-12-31,red wine,200.0,fred,1.0,\"alexis, tata\"",
|
||||||
"2016-12-31,fromage à raclette,10.0,alexis,2.0,\"alexis, fred, tata, pépé\""]
|
"2016-12-31,fromage à raclette,10.0,alexis,2.0,\"alexis, fred, tata, pépé\""]
|
||||||
received_lines = resp.data.split("\n")
|
received_lines = resp.data.decode('utf-8').split("\n")
|
||||||
|
|
||||||
for i, line in enumerate(expected):
|
for i, line in enumerate(expected):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
@ -693,10 +695,10 @@ class BudgetTestCase(TestCase):
|
||||||
'export_format': 'json',
|
'export_format': 'json',
|
||||||
'export_type': 'transactions'
|
'export_type': 'transactions'
|
||||||
})
|
})
|
||||||
expected = [{u"amount": 127.33, u"receiver": u"fred", u"ower": u"alexis"},
|
expected = [{"amount": 127.33, "receiver": "fred", "ower": "alexis"},
|
||||||
{u"amount": 55.34, u"receiver": u"fred", u"ower": u"tata"},
|
{"amount": 55.34, "receiver": "fred", "ower": "tata"},
|
||||||
{u"amount": 2.00, u"receiver": u"fred", u"ower": u"p\xe9p\xe9"}]
|
{"amount": 2.00, "receiver": "fred", "ower": "p\xe9p\xe9"}]
|
||||||
self.assertEqual(json.loads(resp.data), expected)
|
self.assertEqual(json.loads(resp.data.decode('utf-8')), expected)
|
||||||
|
|
||||||
# generate csv export of transactions
|
# generate csv export of transactions
|
||||||
resp = self.app.post("/raclette/edit", data={
|
resp = self.app.post("/raclette/edit", data={
|
||||||
|
@ -708,7 +710,7 @@ class BudgetTestCase(TestCase):
|
||||||
"127.33,fred,alexis",
|
"127.33,fred,alexis",
|
||||||
"55.34,fred,tata",
|
"55.34,fred,tata",
|
||||||
"2.0,fred,pépé"]
|
"2.0,fred,pépé"]
|
||||||
received_lines = resp.data.split("\n")
|
received_lines = resp.data.decode('utf-8').split("\n")
|
||||||
|
|
||||||
for i, line in enumerate(expected):
|
for i, line in enumerate(expected):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
@ -723,7 +725,7 @@ class BudgetTestCase(TestCase):
|
||||||
})
|
})
|
||||||
|
|
||||||
self.assertEqual(resp.status_code, 200)
|
self.assertEqual(resp.status_code, 200)
|
||||||
self.assertIn('id="export_format" name="export_format"', resp.data)
|
self.assertIn('id="export_format" name="export_format"', resp.data.decode('utf-8'))
|
||||||
|
|
||||||
# wrong export_type should return a 200 and export form
|
# wrong export_type should return a 200 and export form
|
||||||
resp = self.app.post("/raclette/edit", data={
|
resp = self.app.post("/raclette/edit", data={
|
||||||
|
@ -732,7 +734,7 @@ class BudgetTestCase(TestCase):
|
||||||
})
|
})
|
||||||
|
|
||||||
self.assertEqual(resp.status_code, 200)
|
self.assertEqual(resp.status_code, 200)
|
||||||
self.assertIn('id="export_format" name="export_format"', resp.data)
|
self.assertIn('id="export_format" name="export_format"', resp.data.decode('utf-8'))
|
||||||
|
|
||||||
|
|
||||||
class APITestCase(TestCase):
|
class APITestCase(TestCase):
|
||||||
|
@ -758,7 +760,7 @@ class APITestCase(TestCase):
|
||||||
def get_auth(self, username, password=None):
|
def get_auth(self, username, password=None):
|
||||||
password = password or username
|
password = password or username
|
||||||
base64string = base64.encodestring(
|
base64string = base64.encodestring(
|
||||||
'%s:%s' % (username, password)).replace('\n', '')
|
('%s:%s' % (username, password)).encode('utf-8')).decode('utf-8').replace('\n', '')
|
||||||
return {"Authorization": "Basic %s" % base64string}
|
return {"Authorization": "Basic %s" % base64string}
|
||||||
|
|
||||||
def assertStatus(self, expected, resp, url=""):
|
def assertStatus(self, expected, resp, url=""):
|
||||||
|
@ -802,7 +804,7 @@ class APITestCase(TestCase):
|
||||||
|
|
||||||
self.assertTrue(400, resp.status_code)
|
self.assertTrue(400, resp.status_code)
|
||||||
self.assertEqual('{"contact_email": ["Invalid email address."]}',
|
self.assertEqual('{"contact_email": ["Invalid email address."]}',
|
||||||
resp.data)
|
resp.data.decode('utf-8'))
|
||||||
|
|
||||||
# create it
|
# create it
|
||||||
resp = self.api_create("raclette")
|
resp = self.api_create("raclette")
|
||||||
|
@ -812,7 +814,7 @@ class APITestCase(TestCase):
|
||||||
resp = self.api_create("raclette")
|
resp = self.api_create("raclette")
|
||||||
|
|
||||||
self.assertTrue(400, resp.status_code)
|
self.assertTrue(400, resp.status_code)
|
||||||
self.assertIn('id', json.loads(resp.data))
|
self.assertIn('id', json.loads(resp.data.decode('utf-8')))
|
||||||
|
|
||||||
# get information about it
|
# get information about it
|
||||||
resp = self.app.get("/api/projects/raclette",
|
resp = self.app.get("/api/projects/raclette",
|
||||||
|
@ -828,7 +830,7 @@ class APITestCase(TestCase):
|
||||||
"id": "raclette",
|
"id": "raclette",
|
||||||
"balance": {},
|
"balance": {},
|
||||||
}
|
}
|
||||||
self.assertDictEqual(json.loads(resp.data), expected)
|
self.assertDictEqual(json.loads(resp.data.decode('utf-8')), expected)
|
||||||
|
|
||||||
# edit should work
|
# edit should work
|
||||||
resp = self.app.put("/api/projects/raclette", data={
|
resp = self.app.put("/api/projects/raclette", data={
|
||||||
|
@ -852,7 +854,7 @@ class APITestCase(TestCase):
|
||||||
"id": "raclette",
|
"id": "raclette",
|
||||||
"balance": {},
|
"balance": {},
|
||||||
}
|
}
|
||||||
self.assertDictEqual(json.loads(resp.data), expected)
|
self.assertDictEqual(json.loads(resp.data.decode('utf-8')), expected)
|
||||||
|
|
||||||
# delete should work
|
# delete should work
|
||||||
resp = self.app.delete("/api/projects/raclette",
|
resp = self.app.delete("/api/projects/raclette",
|
||||||
|
@ -874,7 +876,7 @@ class APITestCase(TestCase):
|
||||||
headers=self.get_auth("raclette"))
|
headers=self.get_auth("raclette"))
|
||||||
|
|
||||||
self.assertStatus(200, req)
|
self.assertStatus(200, req)
|
||||||
self.assertEqual('[]', req.data)
|
self.assertEqual('[]', req.data.decode('utf-8'))
|
||||||
|
|
||||||
# add a member
|
# add a member
|
||||||
req = self.app.post("/api/projects/raclette/members", data={
|
req = self.app.post("/api/projects/raclette/members", data={
|
||||||
|
@ -883,14 +885,14 @@ class APITestCase(TestCase):
|
||||||
|
|
||||||
# the id of the new member should be returned
|
# the id of the new member should be returned
|
||||||
self.assertStatus(201, req)
|
self.assertStatus(201, req)
|
||||||
self.assertEqual("1", req.data)
|
self.assertEqual("1", req.data.decode('utf-8'))
|
||||||
|
|
||||||
# the list of members should contain one member
|
# the list of members should contain one member
|
||||||
req = self.app.get("/api/projects/raclette/members",
|
req = self.app.get("/api/projects/raclette/members",
|
||||||
headers=self.get_auth("raclette"))
|
headers=self.get_auth("raclette"))
|
||||||
|
|
||||||
self.assertStatus(200, req)
|
self.assertStatus(200, req)
|
||||||
self.assertEqual(len(json.loads(req.data)), 1)
|
self.assertEqual(len(json.loads(req.data.decode('utf-8'))), 1)
|
||||||
|
|
||||||
# edit this member
|
# edit this member
|
||||||
req = self.app.put("/api/projects/raclette/members/1", data={
|
req = self.app.put("/api/projects/raclette/members/1", data={
|
||||||
|
@ -904,7 +906,7 @@ class APITestCase(TestCase):
|
||||||
headers=self.get_auth("raclette"))
|
headers=self.get_auth("raclette"))
|
||||||
|
|
||||||
self.assertStatus(200, req)
|
self.assertStatus(200, req)
|
||||||
self.assertEqual("Fred", json.loads(req.data)["name"])
|
self.assertEqual("Fred", json.loads(req.data.decode('utf-8'))["name"])
|
||||||
|
|
||||||
# delete a member
|
# delete a member
|
||||||
|
|
||||||
|
@ -919,7 +921,7 @@ class APITestCase(TestCase):
|
||||||
headers=self.get_auth("raclette"))
|
headers=self.get_auth("raclette"))
|
||||||
|
|
||||||
self.assertStatus(200, req)
|
self.assertStatus(200, req)
|
||||||
self.assertEqual('[]', req.data)
|
self.assertEqual('[]', req.data.decode('utf-8'))
|
||||||
|
|
||||||
def test_bills(self):
|
def test_bills(self):
|
||||||
# create a project
|
# create a project
|
||||||
|
@ -935,12 +937,12 @@ class APITestCase(TestCase):
|
||||||
headers=self.get_auth("raclette"))
|
headers=self.get_auth("raclette"))
|
||||||
self.assertStatus(200, req)
|
self.assertStatus(200, req)
|
||||||
|
|
||||||
self.assertEqual("[]", req.data)
|
self.assertEqual("[]", req.data.decode('utf-8'))
|
||||||
|
|
||||||
# add a bill
|
# add a bill
|
||||||
req = self.app.post("/api/projects/raclette/bills", data={
|
req = self.app.post("/api/projects/raclette/bills", data={
|
||||||
'date': '2011-08-10',
|
'date': '2011-08-10',
|
||||||
'what': u'fromage',
|
'what': 'fromage',
|
||||||
'payer': "1",
|
'payer': "1",
|
||||||
'payed_for': ["1", "2"],
|
'payed_for': ["1", "2"],
|
||||||
'amount': '25',
|
'amount': '25',
|
||||||
|
@ -948,7 +950,7 @@ class APITestCase(TestCase):
|
||||||
|
|
||||||
# should return the id
|
# should return the id
|
||||||
self.assertStatus(201, req)
|
self.assertStatus(201, req)
|
||||||
self.assertEqual(req.data, "1")
|
self.assertEqual(req.data.decode('utf-8'), "1")
|
||||||
|
|
||||||
# get this bill details
|
# get this bill details
|
||||||
req = self.app.get("/api/projects/raclette/bills/1",
|
req = self.app.get("/api/projects/raclette/bills/1",
|
||||||
|
@ -966,30 +968,30 @@ class APITestCase(TestCase):
|
||||||
"date": "2011-08-10",
|
"date": "2011-08-10",
|
||||||
"id": 1}
|
"id": 1}
|
||||||
|
|
||||||
self.assertDictEqual(expected, json.loads(req.data))
|
self.assertDictEqual(expected, json.loads(req.data.decode('utf-8')))
|
||||||
|
|
||||||
# the list of bills should lenght 1
|
# the list of bills should lenght 1
|
||||||
req = self.app.get("/api/projects/raclette/bills",
|
req = self.app.get("/api/projects/raclette/bills",
|
||||||
headers=self.get_auth("raclette"))
|
headers=self.get_auth("raclette"))
|
||||||
self.assertStatus(200, req)
|
self.assertStatus(200, req)
|
||||||
self.assertEqual(1, len(json.loads(req.data)))
|
self.assertEqual(1, len(json.loads(req.data.decode('utf-8'))))
|
||||||
|
|
||||||
# edit with errors should return an error
|
# edit with errors should return an error
|
||||||
req = self.app.put("/api/projects/raclette/bills/1", data={
|
req = self.app.put("/api/projects/raclette/bills/1", data={
|
||||||
'date': '201111111-08-10', # not a date
|
'date': '201111111-08-10', # not a date
|
||||||
'what': u'fromage',
|
'what': 'fromage',
|
||||||
'payer': "1",
|
'payer': "1",
|
||||||
'payed_for': ["1", "2"],
|
'payed_for': ["1", "2"],
|
||||||
'amount': '25',
|
'amount': '25',
|
||||||
}, headers=self.get_auth("raclette"))
|
}, headers=self.get_auth("raclette"))
|
||||||
|
|
||||||
self.assertStatus(400, req)
|
self.assertStatus(400, req)
|
||||||
self.assertEqual('{"date": ["This field is required."]}', req.data)
|
self.assertEqual('{"date": ["This field is required."]}', req.data.decode('utf-8'))
|
||||||
|
|
||||||
# edit a bill
|
# edit a bill
|
||||||
req = self.app.put("/api/projects/raclette/bills/1", data={
|
req = self.app.put("/api/projects/raclette/bills/1", data={
|
||||||
'date': '2011-09-10',
|
'date': '2011-09-10',
|
||||||
'what': u'beer',
|
'what': 'beer',
|
||||||
'payer': "2",
|
'payer': "2",
|
||||||
'payed_for': ["1", "2"],
|
'payed_for': ["1", "2"],
|
||||||
'amount': '25',
|
'amount': '25',
|
||||||
|
@ -1009,7 +1011,7 @@ class APITestCase(TestCase):
|
||||||
"date": "2011-09-10",
|
"date": "2011-09-10",
|
||||||
"id": 1}
|
"id": 1}
|
||||||
|
|
||||||
self.assertDictEqual(expected, json.loads(req.data))
|
self.assertDictEqual(expected, json.loads(req.data.decode('utf-8')))
|
||||||
|
|
||||||
# delete a bill
|
# delete a bill
|
||||||
req = self.app.delete("/api/projects/raclette/bills/1",
|
req = self.app.delete("/api/projects/raclette/bills/1",
|
||||||
|
@ -1031,7 +1033,7 @@ class APITestCase(TestCase):
|
||||||
self.api_add_member("raclette", "<script>")
|
self.api_add_member("raclette", "<script>")
|
||||||
|
|
||||||
result = self.app.get('/raclette/')
|
result = self.app.get('/raclette/')
|
||||||
self.assertNotIn("<script>", result.data)
|
self.assertNotIn("<script>", result.data.decode('utf-8'))
|
||||||
|
|
||||||
def test_weighted_bills(self):
|
def test_weighted_bills(self):
|
||||||
# create a project
|
# create a project
|
||||||
|
@ -1066,7 +1068,7 @@ class APITestCase(TestCase):
|
||||||
"amount": 25.0,
|
"amount": 25.0,
|
||||||
"date": "2011-08-10",
|
"date": "2011-08-10",
|
||||||
"id": 1}
|
"id": 1}
|
||||||
self.assertDictEqual(expected, json.loads(req.data))
|
self.assertDictEqual(expected, json.loads(req.data.decode('utf-8')))
|
||||||
|
|
||||||
# getting it should return a 404
|
# getting it should return a 404
|
||||||
req = self.app.get("/api/projects/raclette",
|
req = self.app.get("/api/projects/raclette",
|
||||||
|
@ -1091,7 +1093,7 @@ class APITestCase(TestCase):
|
||||||
"password": "raclette"}
|
"password": "raclette"}
|
||||||
|
|
||||||
self.assertStatus(200, req)
|
self.assertStatus(200, req)
|
||||||
self.assertEqual(expected, json.loads(req.data))
|
self.assertEqual(expected, json.loads(req.data.decode('utf-8')))
|
||||||
|
|
||||||
class ServerTestCase(APITestCase):
|
class ServerTestCase(APITestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -1108,6 +1110,5 @@ class ServerTestCase(APITestCase):
|
||||||
req = self.app.get("/foo/")
|
req = self.app.get("/foo/")
|
||||||
self.assertStatus(200, req)
|
self.assertStatus(200, req)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import re
|
import re
|
||||||
import inspect
|
import inspect
|
||||||
|
|
||||||
|
from io import BytesIO, StringIO
|
||||||
from jinja2 import filters
|
from jinja2 import filters
|
||||||
from json import dumps
|
from json import dumps
|
||||||
from flask import redirect
|
from flask import redirect
|
||||||
from werkzeug.routing import HTTPException, RoutingException
|
from werkzeug.routing import HTTPException, RoutingException
|
||||||
from io import BytesIO
|
import six
|
||||||
|
|
||||||
import csv
|
import csv
|
||||||
|
|
||||||
|
@ -16,13 +17,14 @@ def slugify(value):
|
||||||
|
|
||||||
Copy/Pasted from ametaireau/pelican/utils itself took from django sources.
|
Copy/Pasted from ametaireau/pelican/utils itself took from django sources.
|
||||||
"""
|
"""
|
||||||
if type(value) == unicode:
|
if isinstance(value, six.text_type):
|
||||||
import unicodedata
|
import unicodedata
|
||||||
value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
|
value = unicodedata.normalize('NFKD', value)
|
||||||
value = unicode(re.sub('[^\w\s-]', '', value).strip().lower())
|
if six.PY2:
|
||||||
|
value = value.encode('ascii', 'ignore')
|
||||||
|
value = six.text_type(re.sub('[^\w\s-]', '', value).strip().lower())
|
||||||
return re.sub('[-\s]+', '-', value)
|
return re.sub('[-\s]+', '-', value)
|
||||||
|
|
||||||
|
|
||||||
class Redirect303(HTTPException, RoutingException):
|
class Redirect303(HTTPException, RoutingException):
|
||||||
"""Raise if the map requests a redirect. This is for example the case if
|
"""Raise if the map requests a redirect. This is for example the case if
|
||||||
`strict_slashes` are activated and an url that requires a trailing slash.
|
`strict_slashes` are activated and an url that requires a trailing slash.
|
||||||
|
@ -86,25 +88,33 @@ def list_of_dicts2json(dict_to_convert):
|
||||||
"""Take a list of dictionnaries and turns it into
|
"""Take a list of dictionnaries and turns it into
|
||||||
a json in-memory file
|
a json in-memory file
|
||||||
"""
|
"""
|
||||||
bytes_io = BytesIO()
|
return BytesIO(dumps(dict_to_convert).encode('utf-8'))
|
||||||
bytes_io.write(dumps(dict_to_convert))
|
|
||||||
bytes_io.seek(0)
|
|
||||||
return bytes_io
|
|
||||||
|
|
||||||
def list_of_dicts2csv(dict_to_convert):
|
def list_of_dicts2csv(dict_to_convert):
|
||||||
"""Take a list of dictionnaries and turns it into
|
"""Take a list of dictionnaries and turns it into
|
||||||
a csv in-memory file, assume all dict have the same keys
|
a csv in-memory file, assume all dict have the same keys
|
||||||
"""
|
"""
|
||||||
bytes_io = BytesIO()
|
# CSV writer has a different behavior in PY2 and PY3
|
||||||
|
# http://stackoverflow.com/a/37974772
|
||||||
try:
|
try:
|
||||||
csv_data = [dict_to_convert[0].keys()]
|
if six.PY3:
|
||||||
for dic in dict_to_convert:
|
csv_file = StringIO()
|
||||||
csv_data.append([dic[h].encode('utf8')
|
csv_data = [dict_to_convert[0].keys()]
|
||||||
if isinstance(dic[h], unicode) else str(dic[h]).encode('utf8')
|
for dic in dict_to_convert:
|
||||||
for h in dict_to_convert[0].keys()])
|
csv_data.append([dic[h] for h in dict_to_convert[0].keys()])
|
||||||
|
else:
|
||||||
|
csv_file = BytesIO()
|
||||||
|
csv_data = []
|
||||||
|
csv_data.append([key.encode('utf-8') for key in dict_to_convert[0].keys()])
|
||||||
|
for dic in dict_to_convert:
|
||||||
|
csv_data.append([dic[h].encode('utf8')
|
||||||
|
if isinstance(dic[h], unicode) else str(dic[h]).encode('utf8')
|
||||||
|
for h in dict_to_convert[0].keys()])
|
||||||
except (KeyError, IndexError):
|
except (KeyError, IndexError):
|
||||||
csv_data = []
|
csv_data = []
|
||||||
writer = csv.writer(bytes_io)
|
writer = csv.writer(csv_file)
|
||||||
writer.writerows(csv_data)
|
writer.writerows(csv_data)
|
||||||
bytes_io.seek(0)
|
csv_file.seek(0)
|
||||||
return bytes_io
|
if six.PY3:
|
||||||
|
csv_file = BytesIO(csv_file.getvalue().encode('utf-8'))
|
||||||
|
return csv_file
|
||||||
|
|
Loading…
Reference in a new issue