mirror of
https://github.com/wger-project/wger.git
synced 2026-02-18 00:17:51 +01:00
Use rir in the 1 RM calculation, if available
This commit is contained in:
@@ -15,6 +15,8 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Standard Library
|
||||
import logging
|
||||
from decimal import Decimal
|
||||
from typing import Optional
|
||||
|
||||
# wger
|
||||
@@ -26,6 +28,9 @@ from wger.trophies.models.user_trophy import UserTrophy
|
||||
from .base import BaseTrophyChecker
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PersonalRecordChecker(BaseTrophyChecker):
|
||||
"""
|
||||
Checker for Personal Record (PR) repeatable trophy.
|
||||
@@ -37,37 +42,33 @@ class PersonalRecordChecker(BaseTrophyChecker):
|
||||
|
||||
"""
|
||||
|
||||
def _estimate_one_rep_max(self):
|
||||
def _estimate_one_rep_max(self) -> float:
|
||||
"""
|
||||
Brzycki's formula: 1RM = weight * (36 / (37 - repetitions))
|
||||
"""
|
||||
log: WorkoutLog | None = self.params.get('log', None)
|
||||
Estimates the user's one-rep max (1RM) using Brzycki's formula:
|
||||
1RM = weight * (36 / (37 - repetitions))
|
||||
|
||||
Note: returning float because of serialization issues with Decimal in JSON.
|
||||
"""
|
||||
log: WorkoutLog | None = self.params.get('log')
|
||||
if not log:
|
||||
raise ValueError('Log should not be None')
|
||||
|
||||
weight = getattr(log, 'weight', None)
|
||||
repetitions = getattr(log, 'repetitions', None)
|
||||
weight = log.weight
|
||||
repetitions = log.repetitions
|
||||
rir = log.rir
|
||||
|
||||
if weight is None:
|
||||
raise ValueError('Weight should not be None')
|
||||
if repetitions is None:
|
||||
raise ValueError('Repetitions should not be None')
|
||||
if rir is not None:
|
||||
repetitions += rir
|
||||
|
||||
try:
|
||||
reps = int(repetitions)
|
||||
except (TypeError, ValueError):
|
||||
raise ValueError('Repetitions must be an integer')
|
||||
|
||||
if reps == 37:
|
||||
if repetitions == 37:
|
||||
raise ValueError("In Brzycki's formula, repetitions cannot be equal to 37.")
|
||||
|
||||
try:
|
||||
w = float(weight)
|
||||
except (TypeError, ValueError):
|
||||
raise ValueError('Weight must be a number')
|
||||
|
||||
return round(w * (36.0 / float(37 - reps)), 2)
|
||||
result = weight * (Decimal('36') / (Decimal('37') - repetitions))
|
||||
return round(float(result), 2)
|
||||
|
||||
def check(self) -> bool:
|
||||
"""Check if user has beaten Personal Record."""
|
||||
@@ -109,43 +110,41 @@ class PersonalRecordChecker(BaseTrophyChecker):
|
||||
return 100.0 if self.check() else 0.0
|
||||
|
||||
def get_context_data(self) -> Optional[dict]:
|
||||
log = self.params.get('log', None)
|
||||
log: WorkoutLog | None = self.params.get('log', None)
|
||||
|
||||
if not log:
|
||||
return None
|
||||
|
||||
session = getattr(log, 'session', None)
|
||||
exercise = getattr(log, 'exercise', None)
|
||||
repetitions_unit = getattr(log, 'repetitions_unit', None)
|
||||
weight_unit = getattr(log, 'weight_unit', None)
|
||||
repetitions = getattr(log, 'repetitions', None)
|
||||
weight = getattr(log, 'weight', None)
|
||||
session = log.session
|
||||
exercise = log.exercise
|
||||
repetitions_unit = log.repetitions_unit
|
||||
weight_unit = log.weight_unit
|
||||
repetitions = log.repetitions
|
||||
weight = log.weight
|
||||
|
||||
try:
|
||||
one_rm_estimate = self._estimate_one_rep_max()
|
||||
except Exception as e:
|
||||
print(f'PR estimation failed : {e}')
|
||||
logger.warning(f'PR estimation failed : {e}')
|
||||
one_rm_estimate = None
|
||||
|
||||
return {
|
||||
'log_id': getattr(log, 'id', None),
|
||||
'date': getattr(log, 'date', None).isoformat(),
|
||||
'session_id': getattr(session, 'id', None) if session else None,
|
||||
'exercise_id': getattr(exercise, 'id', None) if exercise else None,
|
||||
'repetitions_unit_id': getattr(repetitions_unit, 'id', None)
|
||||
if repetitions_unit
|
||||
else None,
|
||||
'log_id': log.id,
|
||||
'date': log.date.isoformat(),
|
||||
'session_id': session.id if session else None,
|
||||
'exercise_id': exercise.id if exercise else None,
|
||||
'repetitions_unit_id': repetitions_unit.id if repetitions_unit else None,
|
||||
'repetitions': float(repetitions) if repetitions else None,
|
||||
'weight_unit_id': getattr(weight_unit, 'id', None) if weight_unit else None,
|
||||
'weight_unit_id': weight_unit.id if weight_unit else None,
|
||||
'weight': float(weight) if weight else None,
|
||||
'iteration': getattr(log, 'iteration', None),
|
||||
'iteration': log.iteration,
|
||||
'one_rep_max_estimate': one_rm_estimate,
|
||||
}
|
||||
|
||||
def get_target_value(self) -> int:
|
||||
def get_target_value(self) -> str:
|
||||
return 'N/A'
|
||||
|
||||
def get_current_value(self) -> int:
|
||||
def get_current_value(self) -> str:
|
||||
return 'N/A'
|
||||
|
||||
def get_progress_display(self) -> str:
|
||||
|
||||
@@ -35,6 +35,7 @@ from wger.trophies.models import (
|
||||
UserTrophy,
|
||||
)
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Trophy settings from WGER_SETTINGS (defined in settings_global.py)
|
||||
|
||||
@@ -26,6 +26,7 @@ from wger.exercises.models.category import ExerciseCategory
|
||||
from wger.manager.models.log import WorkoutLog
|
||||
from wger.trophies.checkers.date_based import DateBasedChecker
|
||||
from wger.trophies.checkers.inactivity_return import InactivityReturnChecker
|
||||
from wger.trophies.checkers.personal_record import PersonalRecordChecker
|
||||
from wger.trophies.checkers.streak import StreakChecker
|
||||
from wger.trophies.checkers.time_based import TimeBasedChecker
|
||||
from wger.trophies.checkers.volume import VolumeChecker
|
||||
@@ -509,16 +510,10 @@ class PersonalRecordCheckerTestCase(WgerTestCase):
|
||||
)
|
||||
|
||||
def test_check_returns_false_with_no_logs(self):
|
||||
# wger
|
||||
from wger.trophies.checkers.personal_record import PersonalRecordChecker
|
||||
|
||||
checker = PersonalRecordChecker(self.user, self.trophy, {})
|
||||
self.assertFalse(checker.check())
|
||||
|
||||
def test_first_log_counts_as_pr(self):
|
||||
# wger
|
||||
from wger.trophies.checkers.personal_record import PersonalRecordChecker
|
||||
|
||||
log = WorkoutLog(user=self.user, exercise=self.exercise, repetitions=10, weight=100)
|
||||
|
||||
checker = PersonalRecordChecker(self.user, self.trophy, {'log': log})
|
||||
@@ -528,9 +523,6 @@ class PersonalRecordCheckerTestCase(WgerTestCase):
|
||||
self.assertIsNotNone(context)
|
||||
|
||||
def test_improvement_detected_and_context_values(self):
|
||||
# wger
|
||||
from wger.trophies.checkers.personal_record import PersonalRecordChecker
|
||||
|
||||
log1 = WorkoutLog(user=self.user, exercise=self.exercise, repetitions=10, weight=100)
|
||||
checker1 = PersonalRecordChecker(self.user, self.trophy, {'log': log1})
|
||||
self.assertTrue(checker1.check())
|
||||
@@ -543,9 +535,6 @@ class PersonalRecordCheckerTestCase(WgerTestCase):
|
||||
self.assertIsNotNone(context)
|
||||
|
||||
def no_award_if_not_an_improvement(self):
|
||||
# wger
|
||||
from wger.trophies.checkers.personal_record import PersonalRecordChecker
|
||||
|
||||
log1 = WorkoutLog(user=self.user, exercise=self.exercise, repetitions=10, weight=100)
|
||||
checker1 = PersonalRecordChecker(self.user, self.trophy, {'log': log1})
|
||||
self.assertTrue(checker1.check())
|
||||
@@ -566,9 +555,6 @@ class PersonalRecordCheckerTestCase(WgerTestCase):
|
||||
self.assertFalse(checker4.check())
|
||||
|
||||
def test_estimate_1rm(self):
|
||||
# wger
|
||||
from wger.trophies.checkers.personal_record import PersonalRecordChecker
|
||||
|
||||
log = WorkoutLog(user=self.user, exercise=self.exercise, repetitions=10, weight=100)
|
||||
one_rm = round(100 * (36.0 / (37 - 10)), 2)
|
||||
|
||||
@@ -577,10 +563,16 @@ class PersonalRecordCheckerTestCase(WgerTestCase):
|
||||
self.assertEqual(estimate, one_rm)
|
||||
self.assertEqual(checker.get_context_data().get('one_rep_max_estimate'), one_rm)
|
||||
|
||||
def test_estimate_1rm_raises_on_missing_values(self):
|
||||
# wger
|
||||
from wger.trophies.checkers.personal_record import PersonalRecordChecker
|
||||
def test_estimate_1rm_with_rir(self):
|
||||
log = WorkoutLog(user=self.user, exercise=self.exercise, repetitions=10, weight=100, rir=2)
|
||||
one_rm = round(100 * (36.0 / (37 - 12)), 2)
|
||||
|
||||
checker = PersonalRecordChecker(self.user, self.trophy, {'log': log})
|
||||
estimate = checker._estimate_one_rep_max()
|
||||
self.assertEqual(estimate, one_rm)
|
||||
self.assertEqual(checker.get_context_data().get('one_rep_max_estimate'), one_rm)
|
||||
|
||||
def test_estimate_1rm_raises_on_missing_values(self):
|
||||
# No log
|
||||
checker = PersonalRecordChecker(self.user, self.trophy, {})
|
||||
with self.assertRaises(ValueError):
|
||||
@@ -602,9 +594,6 @@ class PersonalRecordCheckerTestCase(WgerTestCase):
|
||||
self.assertIsNone(context.get('one_rep_max_estimate'))
|
||||
|
||||
def test_estimate_1rm_raises_on_repetitions_37(self):
|
||||
# wger
|
||||
from wger.trophies.checkers.personal_record import PersonalRecordChecker
|
||||
|
||||
log = WorkoutLog(user=self.user, exercise=self.exercise, weight=100, repetitions=37)
|
||||
checker = PersonalRecordChecker(self.user, self.trophy, {'log': log})
|
||||
with self.assertRaises(ValueError):
|
||||
|
||||
@@ -18,6 +18,7 @@ import logging
|
||||
# wger
|
||||
from wger.core.tests.base_testcase import WgerAccessTestCase
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -25,6 +26,7 @@ class TrophiesOverviewTestCase(WgerAccessTestCase):
|
||||
"""
|
||||
Test case for the trophies overview page
|
||||
"""
|
||||
|
||||
url = 'trophies:admin-overview'
|
||||
anonymous_fail = True
|
||||
user_success = 'admin'
|
||||
@@ -41,5 +43,3 @@ class TrophiesOverviewTestCase(WgerAccessTestCase):
|
||||
'member4',
|
||||
'member5',
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ from django.views.generic import ListView
|
||||
# wger
|
||||
from wger.trophies.models import Trophy
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user