implement user preference for elevation plots starting at zero;

add docker make command to downgrade db;
add (google-powered) translations for the new labels
This commit is contained in:
Joshua Taillon 2023-03-13 22:51:05 -06:00
parent dd40fd7154
commit 34272814c1
19 changed files with 107 additions and 7 deletions

View File

@ -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:

View File

@ -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 ###

View File

@ -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: should elevation plots start at zero by default?
:<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()

View File

@ -59,6 +59,7 @@ 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 +212,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,
}, },

View File

@ -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.START_ELEVATION_AT_ZERO') }}:</dt>
<dd>{{ $t(`common.${startElevationAtZero}`) }}</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')">
@ -54,6 +56,7 @@
: languageLabels['en'] : languageLabels['en']
) )
const fistDayOfWeek = computed(() => (props.user.weekm ? 'MONDAY' : 'SUNDAY')) const fistDayOfWeek = computed(() => (props.user.weekm ? 'MONDAY' : 'SUNDAY'))
const startElevationAtZero = computed(() => (props.user.start_elevation_at_zero ? 'TRUE' : 'FALSE'))
const timezone = computed(() => const timezone = computed(() =>
props.user.timezone ? props.user.timezone : 'Europe/Paris' props.user.timezone ? props.user.timezone : 'Europe/Paris'
) )

View File

@ -99,6 +99,26 @@
</label> </label>
</div> </div>
</div> </div>
<div class="form-items form-checkboxes">
<span class="checkboxes-label">
{{ $t('user.PROFILE.START_ELEVATION_AT_ZERO') }}
</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(`common.${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: 'TRUE',
value: true
},
{
label: 'FALSE',
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
} }

View File

@ -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)
) )

View File

@ -5,6 +5,7 @@
"DAY": "Tag | Tage", "DAY": "Tag | Tage",
"DISPLAYED": "Angezeigt", "DISPLAYED": "Angezeigt",
"DOCUMENTATION": "Dokumentation (en)", "DOCUMENTATION": "Dokumentation (en)",
"FALSE": "Falsch",
"HERE": "hier", "HERE": "hier",
"HIDDEN": "Versteckt", "HIDDEN": "Versteckt",
"HOME": "Startseite", "HOME": "Startseite",
@ -21,5 +22,6 @@
"LABEL": "pro Seite" "LABEL": "pro Seite"
} }
}, },
"TOTAL": "Insgesamt" "TOTAL": "Insgesamt",
"TRUE": "Wahr"
} }

View File

@ -92,6 +92,7 @@
"STOPPED_SPEED_THRESHOLD": "Geschwindigkeitsschwellenwert für Stopp" "STOPPED_SPEED_THRESHOLD": "Geschwindigkeitsschwellenwert für Stopp"
}, },
"SPORTS_EDITION": "Sportarten-Einstellungsausgabe", "SPORTS_EDITION": "Sportarten-Einstellungsausgabe",
"START_ELEVATION_AT_ZERO": "Starten Sie das Höhendiagramm bei null",
"SUCCESSFUL_EMAIL_UPDATE": "Dein Konto wurde erfolgreich aktualisiert. Bitte prüfe Deine E-Mail um die neue E-Mail Adresse zu bestätigen.", "SUCCESSFUL_EMAIL_UPDATE": "Dein Konto wurde erfolgreich aktualisiert. Bitte prüfe Deine E-Mail um die neue E-Mail Adresse zu bestätigen.",
"SUCCESSFUL_REGISTRATION": "Dein Konto wurde erfolgreich erstellt.", "SUCCESSFUL_REGISTRATION": "Dein Konto wurde erfolgreich erstellt.",
"SUCCESSFUL_REGISTRATION_WITH_EMAIL": "Ein Aktivierungslink für Dein Konto wurde an die angegebene E-Mail Adresse geschickt.", "SUCCESSFUL_REGISTRATION_WITH_EMAIL": "Ein Aktivierungslink für Dein Konto wurde an die angegebene E-Mail Adresse geschickt.",

View File

@ -5,6 +5,7 @@
"DAY": "day | days", "DAY": "day | days",
"DISPLAYED": "Displayed", "DISPLAYED": "Displayed",
"DOCUMENTATION": "documentation", "DOCUMENTATION": "documentation",
"FALSE": "False",
"HERE": "here", "HERE": "here",
"HIDDEN": "Hidden", "HIDDEN": "Hidden",
"HOME": "Home", "HOME": "Home",
@ -21,5 +22,6 @@
"LABEL": "par page" "LABEL": "par page"
} }
}, },
"TOTAL": "Total" "TOTAL": "Total",
"TRUE": "True"
} }

View File

@ -92,6 +92,7 @@
"STOPPED_SPEED_THRESHOLD": "stopped speed threshold" "STOPPED_SPEED_THRESHOLD": "stopped speed threshold"
}, },
"SPORTS_EDITION": "Sports preferences edition", "SPORTS_EDITION": "Sports preferences edition",
"START_ELEVATION_AT_ZERO": "Start elevation plots at zero",
"SUCCESSFUL_EMAIL_UPDATE": "Your account has been updated successfully. Please check your email to confirm your new email address.", "SUCCESSFUL_EMAIL_UPDATE": "Your account has been updated successfully. Please check your email to confirm your new email address.",
"SUCCESSFUL_REGISTRATION": "Your account has been created successfully.", "SUCCESSFUL_REGISTRATION": "Your account has been created successfully.",
"SUCCESSFUL_REGISTRATION_WITH_EMAIL": "A link to activate your account has been emailed to the address provided.", "SUCCESSFUL_REGISTRATION_WITH_EMAIL": "A link to activate your account has been emailed to the address provided.",

View File

@ -1,2 +1,4 @@
{ {
"FALSE": "Falso",
"TRUE": "Verdadero"
} }

View File

@ -5,6 +5,7 @@
"DAY": "jour | jours", "DAY": "jour | jours",
"DISPLAYED": "Affiché", "DISPLAYED": "Affiché",
"DOCUMENTATION": "documentation (en)", "DOCUMENTATION": "documentation (en)",
"FALSE": "Faux",
"HERE": "ici", "HERE": "ici",
"HIDDEN": "Masqué", "HIDDEN": "Masqué",
"HOME": "Accueil", "HOME": "Accueil",
@ -21,5 +22,6 @@
"LABEL": "par page" "LABEL": "par page"
} }
}, },
"TOTAL": "Total" "TOTAL": "Total",
"TRUE": "Vrai"
} }

View File

@ -92,6 +92,7 @@
"STOPPED_SPEED_THRESHOLD": "seuil de vitesse arrêtée" "STOPPED_SPEED_THRESHOLD": "seuil de vitesse arrêtée"
}, },
"SPORTS_EDITION": "Mise à jour des préférences des sports", "SPORTS_EDITION": "Mise à jour des préférences des sports",
"START_ELEVATION_AT_ZERO": "Démarrer le tracé d'altitude à zéro",
"SUCCESSFUL_EMAIL_UPDATE": "Votre compte a été modifié avec succès. Veuillez vérifier votre boîte de réception pour valider votre nouvelle adresse électronique.", "SUCCESSFUL_EMAIL_UPDATE": "Votre compte a été modifié avec succès. Veuillez vérifier votre boîte de réception pour valider votre nouvelle adresse électronique.",
"SUCCESSFUL_REGISTRATION": "Votre compte a été créé avec succès.", "SUCCESSFUL_REGISTRATION": "Votre compte a été créé avec succès.",
"SUCCESSFUL_REGISTRATION_WITH_EMAIL": "Un lien pour activer votre compte a été envoyé à l'adresse électronique fournie.", "SUCCESSFUL_REGISTRATION_WITH_EMAIL": "Un lien pour activer votre compte a été envoyé à l'adresse électronique fournie.",

View File

@ -5,6 +5,7 @@
"DAY": "giorno | giorni", "DAY": "giorno | giorni",
"DISPLAYED": "Mostrato", "DISPLAYED": "Mostrato",
"DOCUMENTATION": "documentazione", "DOCUMENTATION": "documentazione",
"FALSE": "Falso",
"HERE": "qui", "HERE": "qui",
"HIDDEN": "Nascosto", "HIDDEN": "Nascosto",
"HOME": "Home", "HOME": "Home",
@ -21,5 +22,6 @@
"LABEL": "per pagina" "LABEL": "per pagina"
} }
}, },
"TOTAL": "Totale" "TOTAL": "Totale",
"TRUE": "Vero"
} }

View File

@ -79,6 +79,7 @@
"STOPPED_SPEED_THRESHOLD": "Limite minimo di velocità" "STOPPED_SPEED_THRESHOLD": "Limite minimo di velocità"
}, },
"SPORTS_EDITION": "Preferenze sport", "SPORTS_EDITION": "Preferenze sport",
"START_ELEVATION_AT_ZERO": "Inizia il grafico dell'elevazione a 0",
"SUCCESSFUL_EMAIL_UPDATE": "Il tuo account è stato aggiornato con successo. Per favore controlla la tua email per confermare il tuo indirizzo email.", "SUCCESSFUL_EMAIL_UPDATE": "Il tuo account è stato aggiornato con successo. Per favore controlla la tua email per confermare il tuo indirizzo email.",
"SUCCESSFUL_REGISTRATION": "Il tuo account è stato creato con successo.", "SUCCESSFUL_REGISTRATION": "Il tuo account è stato creato con successo.",
"SUCCESSFUL_REGISTRATION_WITH_EMAIL": "Un link per attivare il tuo account è stato inviato all'indirizzo specificato.", "SUCCESSFUL_REGISTRATION_WITH_EMAIL": "Un link per attivare il tuo account è stato inviato all'indirizzo specificato.",

View File

@ -5,6 +5,7 @@
"DAY": "dag | dagen", "DAY": "dag | dagen",
"DISPLAYED": "Weergegeven", "DISPLAYED": "Weergegeven",
"DOCUMENTATION": "documentatie", "DOCUMENTATION": "documentatie",
"FALSE": "Waar",
"HERE": "hier", "HERE": "hier",
"HIDDEN": "Verborgen", "HIDDEN": "Verborgen",
"HOME": "Startscherm", "HOME": "Startscherm",
@ -21,5 +22,6 @@
"LABEL": "per pagina" "LABEL": "per pagina"
} }
}, },
"TOTAL": "Totaal" "TOTAL": "Totaal",
"TRUE": "Onwaar"
} }

View File

@ -92,6 +92,7 @@
"STOPPED_SPEED_THRESHOLD": "snelheidsgrens voor stilstand" "STOPPED_SPEED_THRESHOLD": "snelheidsgrens voor stilstand"
}, },
"SPORTS_EDITION": "Sport voorkeuren aanpassen", "SPORTS_EDITION": "Sport voorkeuren aanpassen",
"START_ELEVATION_AT_ZERO": "Begin hoogtegrafiek op nul",
"SUCCESSFUL_EMAIL_UPDATE": "Uw account werd succesvol bijgewerkt. Controleer uw inbox om uw nieuw email adres te bevestigen.", "SUCCESSFUL_EMAIL_UPDATE": "Uw account werd succesvol bijgewerkt. Controleer uw inbox om uw nieuw email adres te bevestigen.",
"SUCCESSFUL_REGISTRATION": "Uw account werd succesvol aangemaakt.", "SUCCESSFUL_REGISTRATION": "Uw account werd succesvol aangemaakt.",
"SUCCESSFUL_REGISTRATION_WITH_EMAIL": "Een link om uw account te activeren werd doorgestuurd naar opgegeven email adres.", "SUCCESSFUL_REGISTRATION_WITH_EMAIL": "Een link om uw account te activeren werd doorgestuurd naar opgegeven email adres.",

View File

@ -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