API & Client: add timezone to user - #11
This commit is contained in:
parent
793ef22c59
commit
fe91040370
@ -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()
|
||||||
|
@ -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(
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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 = {
|
||||||
|
@ -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,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"""empty message
|
"""add static map url to 'Activity' table
|
||||||
|
|
||||||
Revision ID: 5a42db64e872
|
Revision ID: 5a42db64e872
|
||||||
Revises: 92adde6ac0d0
|
Revises: 92adde6ac0d0
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"""empty message
|
"""add static map id to 'Activity' table
|
||||||
|
|
||||||
Revision ID: 9f8c9c37da44
|
Revision ID: 9f8c9c37da44
|
||||||
Revises: 5a42db64e872
|
Revises: 5a42db64e872
|
||||||
|
28
fittrackee_api/migrations/versions/e82e5e9447de_.py
Normal file
28
fittrackee_api/migrations/versions/e82e5e9447de_.py
Normal 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 ###
|
@ -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'
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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 && (
|
||||||
|
@ -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"
|
||||||
|
@ -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',
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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"
|
||||||
|
11
yarn.lock
11
yarn.lock
@ -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"
|
||||||
|
Loading…
Reference in New Issue
Block a user