API & Client: add user langage preferences in database

This commit is contained in:
Sam 2019-09-16 14:19:21 +02:00
parent 0443a4e3c9
commit 1f17b8fed4
17 changed files with 92 additions and 8 deletions

View File

@ -317,6 +317,7 @@
<span class="nt">&quot;email&quot;</span><span class="p">:</span> <span class="s2">&quot;sam@example.com&quot;</span><span class="p">,</span> <span class="nt">&quot;email&quot;</span><span class="p">:</span> <span class="s2">&quot;sam@example.com&quot;</span><span class="p">,</span>
<span class="nt">&quot;first_name&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span> <span class="nt">&quot;first_name&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;id&quot;</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="nt">&quot;id&quot;</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
<span class="nt">&quot;language&quot;</span><span class="p">:</span> <span class="s2">&quot;en&quot;</span><span class="p">,</span>
<span class="nt">&quot;last_name&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span> <span class="nt">&quot;last_name&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;location&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span> <span class="nt">&quot;location&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;nb_activities&quot;</span><span class="p">:</span> <span class="mi">6</span><span class="p">,</span> <span class="nt">&quot;nb_activities&quot;</span><span class="p">:</span> <span class="mi">6</span><span class="p">,</span>
@ -374,6 +375,7 @@
<span class="nt">&quot;email&quot;</span><span class="p">:</span> <span class="s2">&quot;sam@example.com&quot;</span><span class="p">,</span> <span class="nt">&quot;email&quot;</span><span class="p">:</span> <span class="s2">&quot;sam@example.com&quot;</span><span class="p">,</span>
<span class="nt">&quot;first_name&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span> <span class="nt">&quot;first_name&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;id&quot;</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="nt">&quot;id&quot;</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
<span class="nt">&quot;language&quot;</span><span class="p">:</span> <span class="s2">&quot;en&quot;</span><span class="p">,</span>
<span class="nt">&quot;last_name&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span> <span class="nt">&quot;last_name&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;location&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span> <span class="nt">&quot;location&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;nb_activities&quot;</span><span class="p">:</span> <span class="mi">6</span><span class="p">,</span> <span class="nt">&quot;nb_activities&quot;</span><span class="p">:</span> <span class="mi">6</span><span class="p">,</span>
@ -402,6 +404,7 @@
<li><p><strong>password_conf</strong> (<em>string</em>) user password confirmation</p></li> <li><p><strong>password_conf</strong> (<em>string</em>) user password confirmation</p></li>
<li><p><strong>timezone</strong> (<em>string</em>) user time zone</p></li> <li><p><strong>timezone</strong> (<em>string</em>) user time zone</p></li>
<li><p><strong>weekm</strong> (<em>string</em>) does week start on Monday?</p></li> <li><p><strong>weekm</strong> (<em>string</em>) does week start on Monday?</p></li>
<li><p><strong>language</strong> (<em>string</em>) language preferences</p></li>
</ul> </ul>
</dd> </dd>
<dt class="field-even">Request Headers</dt> <dt class="field-even">Request Headers</dt>

File diff suppressed because one or more lines are too long

View File

@ -65,6 +65,7 @@ def user_1_full():
user.last_name = 'Doe' user.last_name = 'Doe'
user.bio = 'just a random guy' user.bio = 'just a random guy'
user.location = 'somewhere' user.location = 'somewhere'
user.language = 'en'
user.timezone = 'America/New_York' user.timezone = 'America/New_York'
user.birth_date = datetime.datetime.strptime('01/01/1980', '%d/%m/%Y') user.birth_date = datetime.datetime.strptime('01/01/1980', '%d/%m/%Y')
db.session.add(user) db.session.add(user)

View File

@ -432,6 +432,7 @@ def test_user_profile_minimal(app, user_1):
assert not data['data']['admin'] assert not data['data']['admin']
assert data['data']['timezone'] is None assert data['data']['timezone'] is None
assert data['data']['weekm'] is False assert data['data']['weekm'] is False
assert data['data']['language'] is None
assert data['data']['nb_activities'] == 0 assert data['data']['nb_activities'] == 0
assert data['data']['nb_sports'] == 0 assert data['data']['nb_sports'] == 0
assert data['data']['total_distance'] == 0 assert data['data']['total_distance'] == 0
@ -467,6 +468,7 @@ def test_user_profile_full(app, user_1_full):
assert data['data']['location'] == 'somewhere' assert data['data']['location'] == 'somewhere'
assert data['data']['timezone'] == 'America/New_York' assert data['data']['timezone'] == 'America/New_York'
assert data['data']['weekm'] is False assert data['data']['weekm'] is False
assert data['data']['language'] == 'en'
assert data['data']['nb_activities'] == 0 assert data['data']['nb_activities'] == 0
assert data['data']['nb_sports'] == 0 assert data['data']['nb_sports'] == 0
assert data['data']['total_distance'] == 0 assert data['data']['total_distance'] == 0
@ -542,6 +544,7 @@ def test_user_profile_valid_update(app, user_1):
password_conf='87654321', password_conf='87654321',
timezone='America/New_York', timezone='America/New_York',
weekm=True, weekm=True,
language='fr',
) )
), ),
headers=dict( headers=dict(
@ -564,6 +567,7 @@ def test_user_profile_valid_update(app, user_1):
assert data['data']['location'] == 'Somewhere' assert data['data']['location'] == 'Somewhere'
assert data['data']['timezone'] == 'America/New_York' assert data['data']['timezone'] == 'America/New_York'
assert data['data']['weekm'] is True assert data['data']['weekm'] is True
assert data['data']['language'] == 'fr'
assert data['data']['nb_activities'] == 0 assert data['data']['nb_activities'] == 0
assert data['data']['nb_sports'] == 0 assert data['data']['nb_sports'] == 0
assert data['data']['total_distance'] == 0 assert data['data']['total_distance'] == 0
@ -589,6 +593,7 @@ def test_user_profile_valid_update_without_password(app, user_1):
birth_date='1980-01-01', birth_date='1980-01-01',
timezone='America/New_York', timezone='America/New_York',
weekm=True, weekm=True,
language='fr',
) )
), ),
headers=dict( headers=dict(
@ -611,6 +616,7 @@ def test_user_profile_valid_update_without_password(app, user_1):
assert data['data']['location'] == 'Somewhere' assert data['data']['location'] == 'Somewhere'
assert data['data']['timezone'] == 'America/New_York' assert data['data']['timezone'] == 'America/New_York'
assert data['data']['weekm'] is True assert data['data']['weekm'] is True
assert data['data']['language'] == 'fr'
assert data['data']['nb_activities'] == 0 assert data['data']['nb_activities'] == 0
assert data['data']['nb_sports'] == 0 assert data['data']['nb_sports'] == 0
assert data['data']['total_distance'] == 0 assert data['data']['total_distance'] == 0
@ -682,6 +688,7 @@ def test_user_profile_invalid_password(app, user_1):
password_conf='876543210', password_conf='876543210',
timezone='America/New_York', timezone='America/New_York',
weekm=True, weekm=True,
language='en',
) )
), ),
headers=dict( headers=dict(
@ -717,6 +724,7 @@ def test_user_profile_missing_password_conf(app, user_1):
password='87654321', password='87654321',
timezone='America/New_York', timezone='America/New_York',
weekm=True, weekm=True,
language='en',
) )
), ),
headers=dict( headers=dict(

View File

@ -45,6 +45,7 @@ def test_single_user(app, user_1):
assert data['data']['location'] is None assert data['data']['location'] is None
assert data['data']['timezone'] is None assert data['data']['timezone'] is None
assert data['data']['weekm'] is False assert data['data']['weekm'] is False
assert data['data']['language'] is None
assert data['data']['nb_activities'] == 0 assert data['data']['nb_activities'] == 0
assert data['data']['nb_sports'] == 0 assert data['data']['nb_sports'] == 0
assert data['data']['total_distance'] == 0 assert data['data']['total_distance'] == 0
@ -90,6 +91,7 @@ def test_single_user_with_activities(
assert data['data']['location'] is None assert data['data']['location'] is None
assert data['data']['timezone'] is None assert data['data']['timezone'] is None
assert data['data']['weekm'] is False assert data['data']['weekm'] is False
assert data['data']['language'] is None
assert data['data']['nb_activities'] == 2 assert data['data']['nb_activities'] == 2
assert data['data']['nb_sports'] == 2 assert data['data']['nb_sports'] == 2
assert data['data']['total_distance'] == 22 assert data['data']['total_distance'] == 22
@ -175,18 +177,21 @@ def test_users_list(app, user_1, user_2, user_3):
assert 'sam@test.com' in data['data']['users'][2]['email'] assert 'sam@test.com' in data['data']['users'][2]['email']
assert data['data']['users'][0]['timezone'] is None assert data['data']['users'][0]['timezone'] is None
assert data['data']['users'][0]['weekm'] is False assert data['data']['users'][0]['weekm'] is False
assert data['data']['users'][0]['language'] is None
assert data['data']['users'][0]['nb_activities'] == 0 assert data['data']['users'][0]['nb_activities'] == 0
assert data['data']['users'][0]['nb_sports'] == 0 assert data['data']['users'][0]['nb_sports'] == 0
assert data['data']['users'][0]['total_distance'] == 0 assert data['data']['users'][0]['total_distance'] == 0
assert data['data']['users'][0]['total_duration'] == '0:00:00' assert data['data']['users'][0]['total_duration'] == '0:00:00'
assert data['data']['users'][1]['timezone'] is None assert data['data']['users'][1]['timezone'] is None
assert data['data']['users'][1]['weekm'] is False assert data['data']['users'][1]['weekm'] is False
assert data['data']['users'][1]['language'] is None
assert data['data']['users'][1]['nb_activities'] == 0 assert data['data']['users'][1]['nb_activities'] == 0
assert data['data']['users'][1]['nb_sports'] == 0 assert data['data']['users'][1]['nb_sports'] == 0
assert data['data']['users'][1]['total_distance'] == 0 assert data['data']['users'][1]['total_distance'] == 0
assert data['data']['users'][1]['total_duration'] == '0:00:00' assert data['data']['users'][1]['total_duration'] == '0:00:00'
assert data['data']['users'][2]['timezone'] is None assert data['data']['users'][2]['timezone'] is None
assert data['data']['users'][2]['weekm'] is True assert data['data']['users'][2]['weekm'] is True
assert data['data']['users'][2]['language'] is None
assert data['data']['users'][2]['nb_activities'] == 0 assert data['data']['users'][2]['nb_activities'] == 0
assert data['data']['users'][2]['nb_sports'] == 0 assert data['data']['users'][2]['nb_sports'] == 0
assert data['data']['users'][2]['total_distance'] == 0 assert data['data']['users'][2]['total_distance'] == 0

View File

@ -14,6 +14,7 @@ def test_user_model(app, user_1):
assert serialized_user['picture'] is False assert serialized_user['picture'] is False
assert serialized_user['timezone'] is None assert serialized_user['timezone'] is None
assert serialized_user['weekm'] is False assert serialized_user['weekm'] is False
assert serialized_user['language'] is None
assert serialized_user['nb_activities'] == 0 assert serialized_user['nb_activities'] == 0
assert serialized_user['nb_sports'] == 0 assert serialized_user['nb_sports'] == 0
assert serialized_user['total_distance'] == 0 assert serialized_user['total_distance'] == 0

View File

@ -331,6 +331,7 @@ def get_user_status(user_id):
"email": "sam@example.com", "email": "sam@example.com",
"first_name": null, "first_name": null,
"id": 2, "id": 2,
"language": "en",
"last_name": null, "last_name": null,
"location": null, "location": null,
"nb_activities": 6, "nb_activities": 6,
@ -388,6 +389,7 @@ def edit_user(user_id):
"email": "sam@example.com", "email": "sam@example.com",
"first_name": null, "first_name": null,
"id": 2, "id": 2,
"language": "en",
"last_name": null, "last_name": null,
"location": null, "location": null,
"nb_activities": 6, "nb_activities": 6,
@ -412,6 +414,7 @@ def edit_user(user_id):
:<json string password_conf: user password confirmation :<json string password_conf: user password confirmation
:<json string timezone: user time zone :<json string timezone: user time zone
:<json string weekm: does week start on Monday? :<json string weekm: does week start on Monday?
:<json string language: language preferences
:reqheader Authorization: OAuth 2.0 Bearer Token :reqheader Authorization: OAuth 2.0 Bearer Token
@ -433,6 +436,7 @@ def edit_user(user_id):
'last_name', 'last_name',
'bio', 'bio',
'birth_date', 'birth_date',
'language',
'location', 'location',
'timezone', 'timezone',
'weekm', 'weekm',
@ -444,6 +448,7 @@ def edit_user(user_id):
last_name = post_data.get('last_name') last_name = post_data.get('last_name')
bio = post_data.get('bio') bio = post_data.get('bio')
birth_date = post_data.get('birth_date') birth_date = post_data.get('birth_date')
language = post_data.get('language')
location = post_data.get('location') location = post_data.get('location')
password = post_data.get('password') password = post_data.get('password')
password_conf = post_data.get('password_conf') password_conf = post_data.get('password_conf')
@ -465,6 +470,7 @@ def edit_user(user_id):
user.first_name = first_name user.first_name = first_name
user.last_name = last_name user.last_name = last_name
user.bio = bio user.bio = bio
user.language = language
user.location = location user.location = location
user.birth_date = ( user.birth_date = (
datetime.datetime.strptime(birth_date, '%Y-%m-%d') datetime.datetime.strptime(birth_date, '%Y-%m-%d')

View File

@ -31,6 +31,7 @@ class User(db.Model):
records = db.relationship( records = db.relationship(
'Record', lazy=True, backref=db.backref('users', lazy='joined') 'Record', lazy=True, backref=db.backref('users', lazy='joined')
) )
language = db.Column(db.String(50), nullable=True)
def __repr__(self): def __repr__(self):
return f'<User {self.username!r}>' return f'<User {self.username!r}>'
@ -116,6 +117,7 @@ class User(db.Model):
'picture': self.picture is not None, 'picture': self.picture is not None,
'timezone': self.timezone, 'timezone': self.timezone,
'weekm': self.weekm, 'weekm': self.weekm,
'language': self.language,
'nb_activities': nb_activity, 'nb_activities': nb_activity,
'nb_sports': len(sports), 'nb_sports': len(sports),
'total_distance': float(total[0]) if total[0] else 0, 'total_distance': float(total[0]) if total[0] else 0,

View File

@ -1,4 +1,4 @@
"""add weekm in 'User' table """add 'weekm' in 'User' table
Revision ID: 27425324c9e3 Revision ID: 27425324c9e3
Revises: 096dd0b43beb Revises: 096dd0b43beb

View File

@ -0,0 +1,28 @@
"""add 'language' to 'User' table
Revision ID: f69f1e413bde
Revises: 27425324c9e3
Create Date: 2019-09-16 13:18:39.198777
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'f69f1e413bde'
down_revision = '27425324c9e3'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('users', sa.Column('language', sa.String(length=50), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('users', 'language')
# ### end Alembic commands ###

View File

@ -1,3 +1,5 @@
import i18next from 'i18next'
import FitTrackeeApi from '../fitTrackeeApi/index' import FitTrackeeApi from '../fitTrackeeApi/index'
import { history } from '../index' import { history } from '../index'
@ -62,3 +64,7 @@ export const deleteData = (target, id) => dispatch => {
}) })
.catch(error => dispatch(setError(`${target}: ${error}`))) .catch(error => dispatch(setError(`${target}: ${error}`)))
} }
export const updateLanguage = language => dispatch => {
i18next.changeLanguage(language).then(dispatch(setLanguage(language)))
}

View File

@ -2,7 +2,7 @@ import FitTrackeeGenericApi from '../fitTrackeeApi'
import FitTrackeeApi from '../fitTrackeeApi/user' import FitTrackeeApi from '../fitTrackeeApi/user'
import { history } from '../index' import { history } from '../index'
import { generateIds } from '../utils' import { generateIds } from '../utils'
import { getOrUpdateData } from './index' import { getOrUpdateData, updateLanguage } from './index'
const AuthError = message => ({ type: 'AUTH_ERROR', message }) const AuthError = message => ({ type: 'AUTH_ERROR', message })
@ -34,6 +34,9 @@ export const getProfile = () => dispatch =>
if (ret.status === 'success') { if (ret.status === 'success') {
dispatch(getOrUpdateData('getData', 'sports')) dispatch(getOrUpdateData('getData', 'sports'))
ret.data.isAuthenticated = true ret.data.isAuthenticated = true
if (ret.data.language) {
dispatch(updateLanguage(ret.data.language))
}
return dispatch(ProfileSuccess(ret.data)) return dispatch(ProfileSuccess(ret.data))
} }
return dispatch(ProfileError(ret.message)) return dispatch(ProfileError(ret.message))

View File

@ -1,12 +1,11 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import i18next from 'i18next'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { ReactComponent as EnFlag } from '../../images/flags/en.svg' import { ReactComponent as EnFlag } from '../../images/flags/en.svg'
import { ReactComponent as FrFlag } from '../../images/flags/fr.svg' import { ReactComponent as FrFlag } from '../../images/flags/fr.svg'
import { setLanguage } from '../../actions/index' import { updateLanguage } from '../../actions/index'
const languages = [ export const languages = [
{ {
name: 'en', name: 'en',
selected: true, selected: true,
@ -69,8 +68,7 @@ export default connect(
dispatch => ({ dispatch => ({
onUpdateLanguage: (lang, selected) => { onUpdateLanguage: (lang, selected) => {
if (lang !== selected) { if (lang !== selected) {
i18next.changeLanguage(lang) dispatch(updateLanguage(lang))
dispatch(setLanguage(lang))
} }
}, },
}) })

View File

@ -60,6 +60,9 @@ function Profile({ message, onDeletePicture, onUploadPicture, t, user }) {
<p> <p>
{t('user:Bio')}: {user.bio} {t('user:Bio')}: {user.bio}
</p> </p>
<p>
{t('user:Language')}: {user.language}
</p>
<p> <p>
{t('user:Timezone')}: {user.timezone} {t('user:Timezone')}: {user.timezone}
</p> </p>

View File

@ -7,6 +7,7 @@ import TimezonePicker from 'react-timezone'
import { handleProfileFormSubmit } from '../../actions/user' import { handleProfileFormSubmit } from '../../actions/user'
import { history } from '../../index' import { history } from '../../index'
import { languages } from '../NavBar/LanguageDropdown'
class ProfileEdit extends React.Component { class ProfileEdit extends React.Component {
constructor(props, context) { constructor(props, context) {
@ -182,6 +183,23 @@ class ProfileEdit extends React.Component {
/> />
</label> </label>
</div> </div>
<div className="form-group">
<label>
{t('user:Language')}:
<select
name="language"
className="form-control input-lg"
value={formData.language}
onChange={e => this.handleFormChange(e)}
>
{languages.map(lang => (
<option value={lang.name} key={lang.name}>
{lang.name}
</option>
))}
</select>
</label>
</div>
<div className="form-group"> <div className="form-group">
<label> <label>
{t('user:Timezone')}: {t('user:Timezone')}:

View File

@ -10,6 +10,7 @@
"Enter the password confirmation": "Enter the password confirmation", "Enter the password confirmation": "Enter the password confirmation",
"First day of week": "First day of week", "First day of week": "First day of week",
"First Name": "First Name", "First Name": "First Name",
"Language": "Language",
"Last Name": "Last Name", "Last Name": "Last Name",
"Location": "Location", "Location": "Location",
"loggedOut": "You are now logged out. Click <1>here</1> to log back in.", "loggedOut": "You are now logged out. Click <1>here</1> to log back in.",

View File

@ -10,6 +10,7 @@
"Enter the password confirmation": "Confirmer le mot de passe", "Enter the password confirmation": "Confirmer le mot de passe",
"First day of week": "Premier jour de la semaine", "First day of week": "Premier jour de la semaine",
"First Name": "Prénom", "First Name": "Prénom",
"Language": "Langue",
"Last Name": "Nom", "Last Name": "Nom",
"Location": "Lieu", "Location": "Lieu",
"loggedOut": "Vous êtes déconnecté. Cliquez <1>ici</1> pour vous reconnecter.", "loggedOut": "Vous êtes déconnecté. Cliquez <1>ici</1> pour vous reconnecter.",