Merge pull request #331 from jat255/add_elevation_chart_preference
Add elevation chart preference
This commit is contained in:
commit
337a882506
Makefile
fittrackee
fittrackee_client/src
components
User
Workout/WorkoutDetail/WorkoutChart
locales
types
3
Makefile
3
Makefile
@ -55,6 +55,9 @@ docker-build-client:
|
|||||||
|
|
||||||
docker-check-all: docker-bandit docker-lint-all docker-type-check docker-test-client docker-test-python
|
docker-check-all: docker-bandit docker-lint-all docker-type-check docker-test-client docker-test-python
|
||||||
|
|
||||||
|
docker-downgrade-db:
|
||||||
|
docker-compose -f docker-compose-dev.yml exec fittrackee $(DOCKER_FLASK) db downgrade --directory $(DOCKER_MIGRATIONS)
|
||||||
|
|
||||||
docker-init: docker-run docker-init-db docker-restart docker-run-workers
|
docker-init: docker-run docker-init-db docker-restart docker-run-workers
|
||||||
|
|
||||||
docker-init-db:
|
docker-init-db:
|
||||||
|
2
fittrackee/dist/index.html
vendored
2
fittrackee/dist/index.html
vendored
@ -1 +1 @@
|
|||||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><!--[if IE]><link rel="icon" href="/favicon.ico"><![endif]--><link rel="stylesheet" href="/static/css/fork-awesome.min.css"/><link rel="stylesheet" href="/static/css/leaflet.css"/><title>FitTrackee</title><script defer="defer" src="/static/js/chunk-vendors.01cb48d3.js"></script><script defer="defer" src="/static/js/app.9225ef92.js"></script><link href="/static/css/app.38b148d9.css" rel="stylesheet"><link rel="icon" type="image/png" sizes="32x32" href="/img/icons/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/img/icons/favicon-16x16.png"><link rel="manifest" href="/manifest.json"><meta name="theme-color" content="#4DBA87"><meta name="apple-mobile-web-app-capable" content="no"><meta name="apple-mobile-web-app-status-bar-style" content="default"><meta name="apple-mobile-web-app-title" content="fittrackee_client"><link rel="apple-touch-icon" href="/img/icons/apple-touch-icon-152x152.png"><link rel="mask-icon" href="/img/icons/safari-pinned-tab.svg" color="#4DBA87"><meta name="msapplication-TileImage" content="/img/icons/msapplication-icon-144x144.png"><meta name="msapplication-TileColor" content="#000000"></head><body><noscript><strong>We're sorry but FitTrackee doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><!--[if IE]><link rel="icon" href="/favicon.ico"><![endif]--><link rel="stylesheet" href="/static/css/fork-awesome.min.css"/><link rel="stylesheet" href="/static/css/leaflet.css"/><title>FitTrackee</title><script defer="defer" src="/static/js/chunk-vendors.01cb48d3.js"></script><script defer="defer" src="/static/js/app.fbd9f0c5.js"></script><link href="/static/css/app.5f8309dc.css" rel="stylesheet"><link rel="icon" type="image/png" sizes="32x32" href="/img/icons/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/img/icons/favicon-16x16.png"><link rel="manifest" href="/manifest.json"><meta name="theme-color" content="#4DBA87"><meta name="apple-mobile-web-app-capable" content="no"><meta name="apple-mobile-web-app-status-bar-style" content="default"><meta name="apple-mobile-web-app-title" content="fittrackee_client"><link rel="apple-touch-icon" href="/img/icons/apple-touch-icon-152x152.png"><link rel="mask-icon" href="/img/icons/safari-pinned-tab.svg" color="#4DBA87"><meta name="msapplication-TileImage" content="/img/icons/msapplication-icon-144x144.png"><meta name="msapplication-TileColor" content="#000000"></head><body><noscript><strong>We're sorry but FitTrackee doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
|
2
fittrackee/dist/service-worker.js
vendored
2
fittrackee/dist/service-worker.js
vendored
File diff suppressed because one or more lines are too long
2
fittrackee/dist/service-worker.js.map
vendored
2
fittrackee/dist/service-worker.js.map
vendored
File diff suppressed because one or more lines are too long
2
fittrackee/dist/static/css/app.38b148d9.css → fittrackee/dist/static/css/app.5f8309dc.css
vendored
2
fittrackee/dist/static/css/app.38b148d9.css → fittrackee/dist/static/css/app.5f8309dc.css
vendored
File diff suppressed because one or more lines are too long
2
fittrackee/dist/static/css/workouts.c2966dd5.css → fittrackee/dist/static/css/workouts.85ff55a9.css
vendored
2
fittrackee/dist/static/css/workouts.c2966dd5.css → fittrackee/dist/static/css/workouts.85ff55a9.css
vendored
File diff suppressed because one or more lines are too long
2
fittrackee/dist/static/js/app.9225ef92.js
vendored
2
fittrackee/dist/static/js/app.9225ef92.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
fittrackee/dist/static/js/app.fbd9f0c5.js
vendored
Normal file
2
fittrackee/dist/static/js/app.fbd9f0c5.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
fittrackee/dist/static/js/app.fbd9f0c5.js.map
vendored
Normal file
1
fittrackee/dist/static/js/app.fbd9f0c5.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
fittrackee/dist/static/js/reset.bf79f188.js → fittrackee/dist/static/js/reset.3d1800dc.js
vendored
2
fittrackee/dist/static/js/reset.bf79f188.js → fittrackee/dist/static/js/reset.3d1800dc.js
vendored
File diff suppressed because one or more lines are too long
2
fittrackee/dist/static/js/reset.bf79f188.js.map → fittrackee/dist/static/js/reset.3d1800dc.js.map
vendored
2
fittrackee/dist/static/js/reset.bf79f188.js.map → fittrackee/dist/static/js/reset.3d1800dc.js.map
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4
fittrackee/dist/static/js/workouts.3abba015.js → fittrackee/dist/static/js/workouts.fd4ab6ab.js
vendored
4
fittrackee/dist/static/js/workouts.3abba015.js → fittrackee/dist/static/js/workouts.fd4ab6ab.js
vendored
File diff suppressed because one or more lines are too long
1
fittrackee/dist/static/js/workouts.fd4ab6ab.js.map
vendored
Normal file
1
fittrackee/dist/static/js/workouts.fd4ab6ab.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -0,0 +1,35 @@
|
|||||||
|
"""add user preference to start elevation plots at zero
|
||||||
|
|
||||||
|
Revision ID: db58d195c5bf
|
||||||
|
Revises: 374a670efe23
|
||||||
|
Create Date: 2023-03-14 04:14:23.781672
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'db58d195c5bf'
|
||||||
|
down_revision = '374a670efe23'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table('users', schema=None) as batch_op:
|
||||||
|
batch_op.add_column(sa.Column('start_elevation_at_zero', sa.Boolean(), nullable=True))
|
||||||
|
op.execute("UPDATE users SET start_elevation_at_zero = true")
|
||||||
|
op.alter_column('users', 'start_elevation_at_zero', nullable=False)
|
||||||
|
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table('users', schema=None) as batch_op:
|
||||||
|
batch_op.drop_column('start_elevation_at_zero')
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
@ -1482,6 +1482,7 @@ class TestUserPreferencesUpdate(ApiTestCaseMixin):
|
|||||||
language=input_language,
|
language=input_language,
|
||||||
imperial_units=True,
|
imperial_units=True,
|
||||||
display_ascent=False,
|
display_ascent=False,
|
||||||
|
start_elevation_at_zero=False,
|
||||||
date_format='yyyy-MM-dd',
|
date_format='yyyy-MM-dd',
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@ -1493,6 +1494,7 @@ class TestUserPreferencesUpdate(ApiTestCaseMixin):
|
|||||||
assert data['status'] == 'success'
|
assert data['status'] == 'success'
|
||||||
assert data['message'] == 'user preferences updated'
|
assert data['message'] == 'user preferences updated'
|
||||||
assert data['data']['display_ascent'] is False
|
assert data['data']['display_ascent'] is False
|
||||||
|
assert data['data']['start_elevation_at_zero'] is False
|
||||||
assert data['data']['imperial_units'] is True
|
assert data['data']['imperial_units'] is True
|
||||||
assert data['data']['language'] == expected_language
|
assert data['data']['language'] == expected_language
|
||||||
assert data['data']['timezone'] == 'America/New_York'
|
assert data['data']['timezone'] == 'America/New_York'
|
||||||
|
@ -869,6 +869,7 @@ def edit_user_preferences(auth_user: User) -> Union[Dict, HttpResponse]:
|
|||||||
4,
|
4,
|
||||||
6
|
6
|
||||||
],
|
],
|
||||||
|
"start_elevation_at_zero": true,
|
||||||
"timezone": "Europe/Paris",
|
"timezone": "Europe/Paris",
|
||||||
"total_distance": 67.895,
|
"total_distance": 67.895,
|
||||||
"total_duration": "6:50:27",
|
"total_duration": "6:50:27",
|
||||||
@ -883,9 +884,9 @@ def edit_user_preferences(auth_user: User) -> Union[Dict, HttpResponse]:
|
|||||||
:<json boolean display_ascent: display highest ascent records and total
|
:<json boolean display_ascent: display highest ascent records and total
|
||||||
:<json boolean imperial_units: display distance in imperial units
|
:<json boolean imperial_units: display distance in imperial units
|
||||||
:<json string language: language preferences
|
:<json string language: language preferences
|
||||||
|
:<json boolean start_elevation_at_zero: do elevation plots start at zero?
|
||||||
:<json string timezone: user time zone
|
:<json string timezone: user time zone
|
||||||
:<json boolean weekm: does week start on Monday?
|
:<json boolean weekm: does week start on Monday?
|
||||||
:<json boolean weekm: does week start on Monday?
|
|
||||||
|
|
||||||
:reqheader Authorization: OAuth 2.0 Bearer Token
|
:reqheader Authorization: OAuth 2.0 Bearer Token
|
||||||
|
|
||||||
@ -906,6 +907,7 @@ def edit_user_preferences(auth_user: User) -> Union[Dict, HttpResponse]:
|
|||||||
'display_ascent',
|
'display_ascent',
|
||||||
'imperial_units',
|
'imperial_units',
|
||||||
'language',
|
'language',
|
||||||
|
'start_elevation_at_zero',
|
||||||
'timezone',
|
'timezone',
|
||||||
'weekm',
|
'weekm',
|
||||||
}
|
}
|
||||||
@ -916,6 +918,7 @@ def edit_user_preferences(auth_user: User) -> Union[Dict, HttpResponse]:
|
|||||||
display_ascent = post_data.get('display_ascent')
|
display_ascent = post_data.get('display_ascent')
|
||||||
imperial_units = post_data.get('imperial_units')
|
imperial_units = post_data.get('imperial_units')
|
||||||
language = get_language(post_data.get('language'))
|
language = get_language(post_data.get('language'))
|
||||||
|
start_elevation_at_zero = post_data.get('start_elevation_at_zero')
|
||||||
timezone = post_data.get('timezone')
|
timezone = post_data.get('timezone')
|
||||||
weekm = post_data.get('weekm')
|
weekm = post_data.get('weekm')
|
||||||
|
|
||||||
@ -924,6 +927,7 @@ def edit_user_preferences(auth_user: User) -> Union[Dict, HttpResponse]:
|
|||||||
auth_user.display_ascent = display_ascent
|
auth_user.display_ascent = display_ascent
|
||||||
auth_user.imperial_units = imperial_units
|
auth_user.imperial_units = imperial_units
|
||||||
auth_user.language = language
|
auth_user.language = language
|
||||||
|
auth_user.start_elevation_at_zero = start_elevation_at_zero
|
||||||
auth_user.timezone = timezone
|
auth_user.timezone = timezone
|
||||||
auth_user.weekm = weekm
|
auth_user.weekm = weekm
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
@ -59,6 +59,9 @@ class User(BaseModel):
|
|||||||
confirmation_token = db.Column(db.String(255), nullable=True)
|
confirmation_token = db.Column(db.String(255), nullable=True)
|
||||||
display_ascent = db.Column(db.Boolean, default=True, nullable=False)
|
display_ascent = db.Column(db.Boolean, default=True, nullable=False)
|
||||||
accepted_policy_date = db.Column(db.DateTime, nullable=True)
|
accepted_policy_date = db.Column(db.DateTime, nullable=True)
|
||||||
|
start_elevation_at_zero = db.Column(
|
||||||
|
db.Boolean, default=True, nullable=False
|
||||||
|
)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'<User {self.username!r}>'
|
return f'<User {self.username!r}>'
|
||||||
@ -211,6 +214,7 @@ class User(BaseModel):
|
|||||||
'display_ascent': self.display_ascent,
|
'display_ascent': self.display_ascent,
|
||||||
'imperial_units': self.imperial_units,
|
'imperial_units': self.imperial_units,
|
||||||
'language': self.language,
|
'language': self.language,
|
||||||
|
'start_elevation_at_zero': self.start_elevation_at_zero,
|
||||||
'timezone': self.timezone,
|
'timezone': self.timezone,
|
||||||
'weekm': self.weekm,
|
'weekm': self.weekm,
|
||||||
},
|
},
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
</dd>
|
</dd>
|
||||||
<dt>{{ $t('user.PROFILE.ASCENT_DATA') }}:</dt>
|
<dt>{{ $t('user.PROFILE.ASCENT_DATA') }}:</dt>
|
||||||
<dd>{{ $t(`common.${display_ascent}`) }}</dd>
|
<dd>{{ $t(`common.${display_ascent}`) }}</dd>
|
||||||
|
<dt>{{ $t('user.PROFILE.ELEVATION_CHART_START.LABEL') }}:</dt>
|
||||||
|
<dd>{{ $t(`user.PROFILE.ELEVATION_CHART_START.${user.start_elevation_at_zero ? 'ZERO' : 'MIN_ALT'}`) }}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
<div class="profile-buttons">
|
<div class="profile-buttons">
|
||||||
<button @click="$router.push('/profile/edit/preferences')">
|
<button @click="$router.push('/profile/edit/preferences')">
|
||||||
|
@ -99,6 +99,26 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-items form-checkboxes">
|
||||||
|
<span class="checkboxes-label">
|
||||||
|
{{ $t('user.PROFILE.ELEVATION_CHART_START.LABEL') }}
|
||||||
|
</span>
|
||||||
|
<div class="checkboxes">
|
||||||
|
<label v-for="status in startElevationAtZeroData" :key="status.label">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
:id="status.label"
|
||||||
|
:name="status.label"
|
||||||
|
:checked="status.value === userForm.start_elevation_at_zero"
|
||||||
|
:disabled="loading"
|
||||||
|
@input="updateStartElevationAtZero(status.value)"
|
||||||
|
/>
|
||||||
|
<span class="checkbox-label">
|
||||||
|
{{ $t(`user.PROFILE.ELEVATION_CHART_START.${status.label}`) }}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-buttons">
|
<div class="form-buttons">
|
||||||
<button class="confirm" type="submit">
|
<button class="confirm" type="submit">
|
||||||
{{ $t('buttons.SUBMIT') }}
|
{{ $t('buttons.SUBMIT') }}
|
||||||
@ -170,6 +190,16 @@
|
|||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
const startElevationAtZeroData = [
|
||||||
|
{
|
||||||
|
label: 'ZERO',
|
||||||
|
value: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'MIN_ALT',
|
||||||
|
value: false
|
||||||
|
}
|
||||||
|
]
|
||||||
const loading = computed(
|
const loading = computed(
|
||||||
() => store.getters[AUTH_USER_STORE.GETTERS.USER_LOADING]
|
() => store.getters[AUTH_USER_STORE.GETTERS.USER_LOADING]
|
||||||
)
|
)
|
||||||
@ -192,6 +222,7 @@
|
|||||||
|
|
||||||
function updateUserForm(user: IAuthUserProfile) {
|
function updateUserForm(user: IAuthUserProfile) {
|
||||||
userForm.display_ascent = user.display_ascent
|
userForm.display_ascent = user.display_ascent
|
||||||
|
userForm.start_elevation_at_zero = user.start_elevation_at_zero ? user.start_elevation_at_zero : false
|
||||||
userForm.imperial_units = user.imperial_units ? user.imperial_units : false
|
userForm.imperial_units = user.imperial_units ? user.imperial_units : false
|
||||||
userForm.language = user.language ? user.language : 'en'
|
userForm.language = user.language ? user.language : 'en'
|
||||||
userForm.timezone = user.timezone ? user.timezone : 'Europe/Paris'
|
userForm.timezone = user.timezone ? user.timezone : 'Europe/Paris'
|
||||||
@ -204,6 +235,9 @@
|
|||||||
function updateTZ(value: string) {
|
function updateTZ(value: string) {
|
||||||
userForm.timezone = value
|
userForm.timezone = value
|
||||||
}
|
}
|
||||||
|
function updateStartElevationAtZero(value: boolean) {
|
||||||
|
userForm.start_elevation_at_zero = value
|
||||||
|
}
|
||||||
function updateAscentDisplay(value: boolean) {
|
function updateAscentDisplay(value: boolean) {
|
||||||
userForm.display_ascent = value
|
userForm.display_ascent = value
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@
|
|||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const displayDistance = ref(true)
|
const displayDistance = ref(true)
|
||||||
const beginElevationAtZero = ref(true)
|
const beginElevationAtZero = ref(props.authUser.start_elevation_at_zero)
|
||||||
const datasets: ComputedRef<IWorkoutChartData> = computed(() =>
|
const datasets: ComputedRef<IWorkoutChartData> = computed(() =>
|
||||||
getDatasets(props.workoutData.chartData, t, props.authUser.imperial_units)
|
getDatasets(props.workoutData.chartData, t, props.authUser.imperial_units)
|
||||||
)
|
)
|
||||||
|
@ -69,6 +69,11 @@
|
|||||||
"EDIT_PREFERENCES": "Edit preferences",
|
"EDIT_PREFERENCES": "Edit preferences",
|
||||||
"EDIT_SPORTS_PREFERENCES": "Edit sports preferences",
|
"EDIT_SPORTS_PREFERENCES": "Edit sports preferences",
|
||||||
"ERRORED_EMAIL_UPDATE": "Please {0} to change your email address again or contact the administrator",
|
"ERRORED_EMAIL_UPDATE": "Please {0} to change your email address again or contact the administrator",
|
||||||
|
"ELEVATION_CHART_START": {
|
||||||
|
"LABEL": "Elevation chart starts at",
|
||||||
|
"ZERO": "Zero",
|
||||||
|
"MIN_ALT": "Minimum altitude"
|
||||||
|
},
|
||||||
"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",
|
"LANGUAGE": "Language",
|
||||||
|
@ -68,6 +68,11 @@
|
|||||||
"EDIT": "Modifier le profil",
|
"EDIT": "Modifier le profil",
|
||||||
"EDIT_PREFERENCES": "Modifier les préférences",
|
"EDIT_PREFERENCES": "Modifier les préférences",
|
||||||
"EDIT_SPORTS_PREFERENCES": "Modifier les préférences des sports",
|
"EDIT_SPORTS_PREFERENCES": "Modifier les préférences des sports",
|
||||||
|
"ELEVATION_CHART_START": {
|
||||||
|
"LABEL": "Début de l'axe pour le graphe affichant l'altitude",
|
||||||
|
"ZERO": "0",
|
||||||
|
"MIN_ALT": "Altitude minimale"
|
||||||
|
},
|
||||||
"ERRORED_EMAIL_UPDATE": "Veuillez vous {0} pour changer de nouveau votre adresse électronique ou contacter l'administrateur",
|
"ERRORED_EMAIL_UPDATE": "Veuillez vous {0} pour changer de nouveau votre adresse électronique ou contacter l'administrateur",
|
||||||
"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",
|
||||||
|
@ -28,6 +28,7 @@ export interface IAuthUserProfile extends IUserProfile {
|
|||||||
accepted_privacy_policy: boolean
|
accepted_privacy_policy: boolean
|
||||||
display_ascent: boolean
|
display_ascent: boolean
|
||||||
imperial_units: boolean
|
imperial_units: boolean
|
||||||
|
start_elevation_at_zero: boolean
|
||||||
language: string | null
|
language: string | null
|
||||||
timezone: string
|
timezone: string
|
||||||
date_format: string
|
date_format: string
|
||||||
@ -63,6 +64,7 @@ export interface IAdminUserPayload {
|
|||||||
|
|
||||||
export interface IUserPreferencesPayload {
|
export interface IUserPreferencesPayload {
|
||||||
display_ascent: boolean
|
display_ascent: boolean
|
||||||
|
start_elevation_at_zero: boolean
|
||||||
imperial_units: boolean
|
imperial_units: boolean
|
||||||
language: string
|
language: string
|
||||||
timezone: string
|
timezone: string
|
||||||
|
Loading…
x
Reference in New Issue
Block a user