mirror of
https://github.com/joaovitoriasilva/endurain.git
synced 2026-01-10 08:17:59 -05:00
Merge branch 'pr/390' into 0.16.0
This commit is contained in:
@@ -383,14 +383,21 @@ def transform_activity_streams_hr(activity_stream, activity, db):
|
||||
|
||||
# Get the user details to calculate heart rate zones
|
||||
detail_user = users_crud.get_user_by_id(activity.user_id, db)
|
||||
if not detail_user or not detail_user.birthdate:
|
||||
# If user details are not available or birthdate is missing, return the activity stream as is
|
||||
if not detail_user:
|
||||
# If user details are not available, return the activity stream as is
|
||||
return activity_stream
|
||||
|
||||
# Calculate the maximum heart rate based on the user's birthdate
|
||||
year = int(detail_user.birthdate.split("-")[0])
|
||||
current_year = datetime.datetime.now().year
|
||||
max_heart_rate = 220 - (current_year - year)
|
||||
# Use user's max_heart_rate if set, otherwise calculate based on age formula
|
||||
if detail_user.max_heart_rate:
|
||||
max_heart_rate = detail_user.max_heart_rate
|
||||
elif detail_user.birthdate:
|
||||
# Calculate the maximum heart rate based on the user's birthdate
|
||||
year = int(detail_user.birthdate.split("-")[0])
|
||||
current_year = datetime.datetime.now().year
|
||||
max_heart_rate = 220 - (current_year - year)
|
||||
else:
|
||||
# If neither max_heart_rate nor birthdate is available, return the activity stream as is
|
||||
return activity_stream
|
||||
|
||||
# Calculate heart rate zones based on the maximum heart rate
|
||||
zone_1 = max_heart_rate * 0.6
|
||||
|
||||
@@ -700,6 +700,17 @@ def upgrade() -> None:
|
||||
op.f("ix_health_sleep_user_id"), "health_sleep", ["user_id"], unique=False
|
||||
)
|
||||
|
||||
# Add max_heart_rate column to users table
|
||||
op.add_column(
|
||||
"users",
|
||||
sa.Column(
|
||||
"max_heart_rate",
|
||||
sa.Integer(),
|
||||
nullable=True,
|
||||
comment="User maximum heart rate (bpm)",
|
||||
),
|
||||
)
|
||||
|
||||
# Update health_weight source column to "garmin" where not null
|
||||
connection.execute(
|
||||
sa.text(
|
||||
@@ -715,6 +726,9 @@ def upgrade() -> None:
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
# Remove max_heart_rate column from users table
|
||||
op.drop_column("users", "max_heart_rate")
|
||||
|
||||
# Drop table health_sleep
|
||||
op.drop_index(op.f("ix_health_sleep_user_id"), table_name="health_sleep")
|
||||
op.drop_index(op.f("ix_health_sleep_date"), table_name="health_sleep")
|
||||
|
||||
@@ -98,6 +98,9 @@ class User(Base):
|
||||
comment="User units (one digit)(1 - metric, 2 - imperial)",
|
||||
)
|
||||
height = Column(Integer, nullable=True, comment="User height in centimeters")
|
||||
max_heart_rate = Column(
|
||||
Integer, nullable=True, comment="User maximum heart rate (bpm)"
|
||||
)
|
||||
access_type = Column(
|
||||
Integer, nullable=False, comment="User type (one digit)(1 - user, 2 - admin)"
|
||||
)
|
||||
|
||||
@@ -132,6 +132,7 @@ class UserBase(BaseModel):
|
||||
gender: Gender = Gender.MALE
|
||||
units: server_settings_schema.Units = server_settings_schema.Units.METRIC
|
||||
height: int | None = None
|
||||
max_heart_rate: int | None = None
|
||||
first_day_of_week: WeekDay = WeekDay.MONDAY
|
||||
currency: server_settings_schema.Currency = server_settings_schema.Currency.EURO
|
||||
|
||||
|
||||
22
frontend/app/package-lock.json
generated
22
frontend/app/package-lock.json
generated
@@ -125,6 +125,7 @@
|
||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.5",
|
||||
@@ -1762,6 +1763,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
@@ -1805,6 +1807,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -2454,6 +2457,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.7.2.tgz",
|
||||
"integrity": "sha512-yxtOBWDrdi5DD5o1pmVdq3WMCvnobT0LU6R8RyyVXPvFRd2o79/0NCuQoCjNTeZz9EzA9xS3JxNWfv54RIHFEA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-common-types": "6.7.2"
|
||||
},
|
||||
@@ -2784,6 +2788,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
@@ -3376,6 +3381,7 @@
|
||||
"integrity": "sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.48.0",
|
||||
"@typescript-eslint/types": "8.48.0",
|
||||
@@ -4012,6 +4018,7 @@
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -4348,6 +4355,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.8.25",
|
||||
"caniuse-lite": "^1.0.30001754",
|
||||
@@ -4482,6 +4490,7 @@
|
||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz",
|
||||
"integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@kurkle/color": "^0.3.0"
|
||||
},
|
||||
@@ -5159,6 +5168,7 @@
|
||||
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
@@ -5219,6 +5229,7 @@
|
||||
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"eslint-config-prettier": "bin/cli.js"
|
||||
},
|
||||
@@ -5266,6 +5277,7 @@
|
||||
"integrity": "sha512-nA5yUs/B1KmKzvC42fyD0+l9Yd+LtEpVhWRbXuDj0e+ZURcTtyRbMDWUeJmTAh2wC6jC83raS63anNM2YT3NPw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"natural-compare": "^1.4.0",
|
||||
@@ -7553,6 +7565,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.11",
|
||||
"picocolors": "^1.1.1",
|
||||
@@ -7857,6 +7870,7 @@
|
||||
"integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.8"
|
||||
},
|
||||
@@ -8742,6 +8756,7 @@
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -8941,6 +8956,7 @@
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -9131,6 +9147,7 @@
|
||||
"integrity": "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.5.0",
|
||||
@@ -9255,6 +9272,7 @@
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -9365,6 +9383,7 @@
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.25.tgz",
|
||||
"integrity": "sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.5.25",
|
||||
"@vue/compiler-sfc": "3.5.25",
|
||||
@@ -9394,6 +9413,7 @@
|
||||
"integrity": "sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"debug": "^4.4.0",
|
||||
"eslint-scope": "^8.2.0",
|
||||
@@ -9848,6 +9868,7 @@
|
||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
@@ -9975,6 +9996,7 @@
|
||||
"integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"rollup": "dist/bin/rollup"
|
||||
},
|
||||
|
||||
@@ -142,6 +142,15 @@
|
||||
</span>
|
||||
<span v-else>{{ $t('generalItems.labelNotApplicable') }}</span>
|
||||
</p>
|
||||
<!-- user max heart rate -->
|
||||
<p>
|
||||
<font-awesome-icon :icon="['fas', 'heart-pulse']" class="me-2" />
|
||||
<b>{{ $t('settingsUserProfileZone.maxHeartRateLabel') }}: </b>
|
||||
<span v-if="authStore.user.max_heart_rate"
|
||||
>{{ authStore.user.max_heart_rate }} {{ $t('generalItems.unitsBpm') }}</span
|
||||
>
|
||||
<span v-else>{{ $t('generalItems.labelNotApplicable') }}</span>
|
||||
</p>
|
||||
<!-- user preferred language -->
|
||||
<p>
|
||||
<font-awesome-icon :icon="['fas', 'language']" class="me-2" />
|
||||
|
||||
@@ -310,6 +310,27 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- max heart rate fields -->
|
||||
<label for="userMaxHeartRateAddEdit"
|
||||
><b>{{
|
||||
$t('usersAddEditUserModalComponent.addEditUserModalMaxHeartRateLabel')
|
||||
}}</b></label
|
||||
>
|
||||
<div class="input-group">
|
||||
<input
|
||||
class="form-control"
|
||||
type="number"
|
||||
name="userMaxHeartRateAddEdit"
|
||||
:placeholder="
|
||||
$t('usersAddEditUserModalComponent.addEditUserModalMaxHeartRatePlaceholder')
|
||||
"
|
||||
v-model="newEditUserMaxHeartRate"
|
||||
min="100"
|
||||
max="250"
|
||||
step="1"
|
||||
/>
|
||||
<span class="input-group-text">{{ $t('generalItems.unitsBpm') }}</span>
|
||||
</div>
|
||||
<!-- preferred language fields -->
|
||||
<label for="userPreferredLanguageAddEdit"
|
||||
><b
|
||||
@@ -499,6 +520,7 @@ const newEditUserCurrency = ref(serverSettingsStore.serverSettings.currency)
|
||||
const newEditUserHeightCms = ref(null)
|
||||
const newEditUserHeightFeet = ref(null)
|
||||
const newEditUserHeightInches = ref(null)
|
||||
const newEditUserMaxHeartRate = ref(null)
|
||||
const newEditUserFirstDayOfWeek = ref(1)
|
||||
const isFeetValid = computed(
|
||||
() => newEditUserHeightFeet.value >= 0 && newEditUserHeightFeet.value <= 10
|
||||
@@ -540,6 +562,7 @@ if (props.user) {
|
||||
newEditUserUnits.value = props.user.units
|
||||
newEditUserCurrency.value = props.user.currency
|
||||
newEditUserHeightCms.value = props.user.height
|
||||
newEditUserMaxHeartRate.value = props.user.max_heart_rate
|
||||
newEditUserPreferredLanguage.value = props.user.preferred_language
|
||||
newEditUserFirstDayOfWeek.value = props.user.first_day_of_week
|
||||
newEditUserAccessType.value = props.user.access_type
|
||||
@@ -648,6 +671,7 @@ async function submitAddUserForm() {
|
||||
units: newEditUserUnits.value,
|
||||
currency: newEditUserCurrency.value,
|
||||
height: newEditUserHeightCms.value,
|
||||
max_heart_rate: newEditUserMaxHeartRate.value,
|
||||
access_type: newEditUserAccessType.value,
|
||||
photo_path: null,
|
||||
first_day_of_week: newEditUserFirstDayOfWeek.value,
|
||||
@@ -691,6 +715,7 @@ async function submitEditUserForm() {
|
||||
units: newEditUserUnits.value,
|
||||
currency: newEditUserCurrency.value,
|
||||
height: newEditUserHeightCms.value,
|
||||
max_heart_rate: newEditUserMaxHeartRate.value,
|
||||
preferred_language: newEditUserPreferredLanguage.value,
|
||||
first_day_of_week: newEditUserFirstDayOfWeek.value,
|
||||
access_type: newEditUserAccessType.value,
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"unitsOption2": "Imperial",
|
||||
"currencyLabel": "Währung",
|
||||
"heightLabel": "Höhe",
|
||||
"maxHeartRateLabel": "Maximale Herzfrequenz",
|
||||
"preferredLanguageLabel": "Bevorzugte Sprache",
|
||||
"firstDayOfWeekLabel": "Erster Tag der Woche",
|
||||
"accessTypeLabel": "Zugangsart",
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
"addEditUserModalCurrencyLabel": "Währung",
|
||||
"addEditUserModalHeightLabel": "Größe",
|
||||
"addEditUserModalHeightPlaceholder": "Größe",
|
||||
"addEditUserModalMaxHeartRateLabel": "Maximale Herzfrequenz (optional)",
|
||||
"addEditUserModalMaxHeartRatePlaceholder": "Maximale Herzfrequenz",
|
||||
"addEditUserModalFeetValidationLabel": "Ungültige Höhe. Bitte geben Sie eine gültige Höhe in Fuß ein.",
|
||||
"addEditUserModalInchesValidationLabel": "Ungültige Höhe. Bitte geben Sie eine gültige Höhe in Zoll ein.",
|
||||
"addEditUserModalUserPreferredLanguageLabel": "Bevorzugte Sprache",
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"unitsOption2": "Imperial",
|
||||
"currencyLabel": "Currency",
|
||||
"heightLabel": "Height",
|
||||
"maxHeartRateLabel": "Max heart rate",
|
||||
"preferredLanguageLabel": "Preferred language",
|
||||
"firstDayOfWeekLabel": "First day of week",
|
||||
"accessTypeLabel": "Access type",
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
"addEditUserModalCurrencyLabel": "Currency",
|
||||
"addEditUserModalHeightLabel": "Height",
|
||||
"addEditUserModalHeightPlaceholder": "Height",
|
||||
"addEditUserModalMaxHeartRateLabel": "Max heart rate (optional)",
|
||||
"addEditUserModalMaxHeartRatePlaceholder": "Max heart rate",
|
||||
"addEditUserModalFeetValidationLabel": "Invalid height. Please enter a valid height in feet.",
|
||||
"addEditUserModalInchesValidationLabel": "Invalid height. Please enter a valid height in inches.",
|
||||
"addEditUserModalUserPreferredLanguageLabel": "Preferred language",
|
||||
|
||||
Reference in New Issue
Block a user