API & Client: add timezone to user - #11

This commit is contained in:
Sam 2018-06-11 15:10:18 +02:00
parent 793ef22c59
commit fe91040370
17 changed files with 98 additions and 4 deletions

View File

@ -49,6 +49,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.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)
db.session.commit() db.session.commit()

View File

@ -397,6 +397,7 @@ def test_user_profile_minimal(app, user_1):
assert data['data']['email'] == 'test@test.com' assert data['data']['email'] == 'test@test.com'
assert data['data']['created_at'] assert data['data']['created_at']
assert not data['data']['admin'] assert not data['data']['admin']
assert data['data']['timezone'] 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
@ -434,6 +435,7 @@ def test_user_profile_full(app, user_1_full):
assert data['data']['birth_date'] assert data['data']['birth_date']
assert data['data']['bio'] == 'just a random guy' assert data['data']['bio'] == 'just a random guy'
assert data['data']['location'] == 'somewhere' assert data['data']['location'] == 'somewhere'
assert data['data']['timezone'] == 'America/New_York'
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
@ -469,6 +471,7 @@ def test_user_profile_with_activities(
assert data['data']['email'] == 'test@test.com' assert data['data']['email'] == 'test@test.com'
assert data['data']['created_at'] assert data['data']['created_at']
assert not data['data']['admin'] assert not data['data']['admin']
assert data['data']['timezone'] 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
@ -507,7 +510,8 @@ def test_user_profile_valid_update(app, user_1):
bio='just a random guy', bio='just a random guy',
birth_date='1980-01-01', birth_date='1980-01-01',
password='87654321', password='87654321',
password_conf='87654321' password_conf='87654321',
timezone='America/New_York'
)), )),
headers=dict( headers=dict(
Authorization='Bearer ' + json.loads( Authorization='Bearer ' + json.loads(
@ -627,7 +631,8 @@ def test_user_profile_invalid_password(app, user_1):
bio='just a random guy', bio='just a random guy',
birth_date='1980-01-01', birth_date='1980-01-01',
password='87654321', password='87654321',
password_conf='876543210' password_conf='876543210',
timezone='America/New_York'
)), )),
headers=dict( headers=dict(
Authorization='Bearer ' + json.loads( Authorization='Bearer ' + json.loads(
@ -661,6 +666,7 @@ def test_user_profile_missing_password_conf(app, user_1):
bio='just a random guy', bio='just a random guy',
birth_date='1980-01-01', birth_date='1980-01-01',
password='87654321', password='87654321',
timezone='America/New_York'
)), )),
headers=dict( headers=dict(
Authorization='Bearer ' + json.loads( Authorization='Bearer ' + json.loads(

View File

@ -32,6 +32,7 @@ def test_single_user(app, user_1):
assert data['data']['birth_date'] is None assert data['data']['birth_date'] is None
assert data['data']['bio'] is None assert data['data']['bio'] is None
assert data['data']['location'] is None assert data['data']['location'] is None
assert data['data']['timezone'] 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
@ -60,6 +61,7 @@ def test_single_user_with_activities(
assert data['data']['birth_date'] is None assert data['data']['birth_date'] is None
assert data['data']['bio'] is None assert data['data']['bio'] is None
assert data['data']['location'] is None assert data['data']['location'] is None
assert data['data']['timezone'] 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
@ -105,10 +107,12 @@ def test_users_list(app, user_1, user_2):
assert 'toto' in data['data']['users'][1]['username'] assert 'toto' in data['data']['users'][1]['username']
assert 'test@test.com' in data['data']['users'][0]['email'] assert 'test@test.com' in data['data']['users'][0]['email']
assert 'toto@toto.com' in data['data']['users'][1]['email'] assert 'toto@toto.com' in data['data']['users'][1]['email']
assert data['data']['users'][0]['timezone'] 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]['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

View File

@ -12,6 +12,7 @@ def test_user_model(app, user_1):
assert serialized_user['location'] is None assert serialized_user['location'] is None
assert serialized_user['birth_date'] is None assert serialized_user['birth_date'] is None
assert serialized_user['picture'] is False assert serialized_user['picture'] is False
assert serialized_user['timezone'] 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

@ -185,6 +185,7 @@ def edit_user(user_id):
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')
timezone = post_data.get('timezone')
if password is not None and password != '': if password is not None and password != '':
if password_conf != password: if password_conf != password:
@ -211,6 +212,7 @@ def edit_user(user_id):
) )
if password is not None and password != '': if password is not None and password != '':
user.password = password user.password = password
user.timezone = timezone
db.session.commit() db.session.commit()
response_object = { response_object = {

View File

@ -22,6 +22,7 @@ class User(db.Model):
location = db.Column(db.String(80), nullable=True) location = db.Column(db.String(80), nullable=True)
bio = db.Column(db.String(200), nullable=True) bio = db.Column(db.String(200), nullable=True)
picture = db.Column(db.String(255), nullable=True) picture = db.Column(db.String(255), nullable=True)
timezone = db.Column(db.String(50), nullable=True)
activities = db.relationship('Activity', activities = db.relationship('Activity',
lazy=True, lazy=True,
backref=db.backref('users', lazy='joined')) backref=db.backref('users', lazy='joined'))
@ -111,6 +112,7 @@ class User(db.Model):
'location': self.location, 'location': self.location,
'birth_date': self.birth_date, 'birth_date': self.birth_date,
'picture': self.picture is not None, 'picture': self.picture is not None,
'timezone': self.timezone,
'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 @@
"""empty message """add static map url to 'Activity' table
Revision ID: 5a42db64e872 Revision ID: 5a42db64e872
Revises: 92adde6ac0d0 Revises: 92adde6ac0d0

View File

@ -1,4 +1,4 @@
"""empty message """add static map id to 'Activity' table
Revision ID: 9f8c9c37da44 Revision ID: 9f8c9c37da44
Revises: 5a42db64e872 Revises: 5a42db64e872

View File

@ -0,0 +1,28 @@
"""add 'timezone' to 'User' table
Revision ID: e82e5e9447de
Revises: 9f8c9c37da44
Create Date: 2018-06-08 16:01:52.684935
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'e82e5e9447de'
down_revision = '9f8c9c37da44'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('users', sa.Column('timezone', sa.String(length=50), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('users', 'timezone')
# ### end Alembic commands ###

View File

@ -26,6 +26,7 @@ def init_data():
email='admin@example.com', email='admin@example.com',
password='mpwoadmin') password='mpwoadmin')
admin.admin = True admin.admin = True
admin.timezone = 'Europe/Paris'
db.session.add(admin) db.session.add(admin)
sport = Sport(label='Cycling (Sport)') sport = Sport(label='Cycling (Sport)')
sport.img = '/img/sports/cycling-sport.png' sport.img = '/img/sports/cycling-sport.png'

View File

@ -208,6 +208,15 @@ input, textarea {
max-width: 45px; max-width: 45px;
max-height: 45px; max-height: 45px;
} }
.timezone-picker {
padding: 0;
}
.timezone-picker-textfield {
font-size: 15px;
}
.unlink { .unlink {
color: black; color: black;
} }

View File

@ -43,6 +43,7 @@ function Profile ({ message, onDeletePicture, onUploadPicture, user }) {
<p>Birth Date: {user.birthDate}</p> <p>Birth Date: {user.birthDate}</p>
<p>Location: {user.location}</p> <p>Location: {user.location}</p>
<p>Bio: {user.bio}</p> <p>Bio: {user.bio}</p>
<p>Time zone: {user.timezone}</p>
</div> </div>
<div className="col-md-4"> <div className="col-md-4">
{ user.picture === true && ( { user.picture === true && (

View File

@ -1,6 +1,7 @@
import React from 'react' import React from 'react'
import { Helmet } from 'react-helmet' import { Helmet } from 'react-helmet'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import TimezonePicker from 'react-timezone'
import { import {
initProfileForm, initProfileForm,
@ -151,6 +152,27 @@ class ProfileEdit extends React.Component {
/> />
</label> </label>
</div> </div>
<div className="form-group">
<label>
Timezone:
<TimezonePicker
className="form-control"
onChange={tz => {
const e = { target:
{
name: 'timezone',
value: tz ? tz : 'Europe/Paris'
}
}
onHandleFormChange(e)
}}
value={formProfile.timezone
? formProfile.timezone
: 'Europe/Paris'
}
/>
</label>
</div>
<input <input
type="submit" type="submit"
className="btn btn-primary btn-lg btn-block" className="btn btn-primary btn-lg btn-block"

View File

@ -53,6 +53,7 @@ export default class FitTrackeeApi {
birth_date: form.birthDate, birth_date: form.birthDate,
password: form.password, password: form.password,
password_conf: form.passwordConf, password_conf: form.passwordConf,
timezone: form.timezone,
}, },
type: 'application/json', type: 'application/json',
} }

View File

@ -87,6 +87,7 @@ const formProfile = (state = initial.formProfile, action) => {
birthDate: action.user.birthDate, birthDate: action.user.birthDate,
location: action.user.location, location: action.user.location,
bio: action.user.bio, bio: action.user.bio,
timezone: action.user.timezone,
}, },
} }
case 'PROFILE_SUCCESS': case 'PROFILE_SUCCESS':
@ -185,6 +186,9 @@ const user = (state = initial.user, action) => {
picture: action.message.data.picture === true picture: action.message.data.picture === true
? action.message.data.picture ? action.message.data.picture
: false, : false,
timezone: action.message.data.timezone
? action.message.data.timezone
: '',
nbActivities: action.message.data.nb_activities, nbActivities: action.message.data.nb_activities,
nbSports: action.message.data.nb_sports, nbSports: action.message.data.nb_sports,
totalDistance: action.message.data.total_distance, totalDistance: action.message.data.total_distance,

View File

@ -17,6 +17,7 @@
"react-router-dom": "^4.2.2", "react-router-dom": "^4.2.2",
"react-router-redux": "^5.0.0-alpha.9", "react-router-redux": "^5.0.0-alpha.9",
"react-scripts": "1.1.4", "react-scripts": "1.1.4",
"react-timezone": "^1.0.5",
"recharts": "^1.0.0-beta.10", "recharts": "^1.0.0-beta.10",
"redux": "4.0.0", "redux": "4.0.0",
"redux-thunk": "^2.2.0" "redux-thunk": "^2.2.0"

View File

@ -1874,6 +1874,10 @@ classnames@2.2.5:
version "2.2.5" version "2.2.5"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d"
classnames@^2.2.5:
version "2.2.6"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
clean-css@4.1.x: clean-css@4.1.x:
version "4.1.11" version "4.1.11"
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.1.11.tgz#2ecdf145aba38f54740f26cefd0ff3e03e125d6a" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.1.11.tgz#2ecdf145aba38f54740f26cefd0ff3e03e125d6a"
@ -7267,6 +7271,13 @@ react-smooth@1.0.0:
raf "^3.2.0" raf "^3.2.0"
react-transition-group "^2.2.1" react-transition-group "^2.2.1"
react-timezone@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/react-timezone/-/react-timezone-1.0.5.tgz#74ba4c9b2235ad8de3bb1303b66f70e6889337e8"
dependencies:
classnames "^2.2.5"
prop-types "^15.6.0"
react-transition-group@^2.2.1: react-transition-group@^2.2.1:
version "2.3.1" version "2.3.1"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.3.1.tgz#31d611b33e143a5e0f2d94c348e026a0f3b474b6" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.3.1.tgz#31d611b33e143a5e0f2d94c348e026a0f3b474b6"