mirror of
https://github.com/wger-project/wger.git
synced 2026-02-18 00:17:51 +01:00
@@ -101,6 +101,8 @@ WGER_SETTINGS["SYNC_INGREDIENTS_CELERY"] = env.bool("SYNC_INGREDIENTS_CELERY", F
|
||||
WGER_SETTINGS["SYNC_OFF_DAILY_DELTA_CELERY"] = env.bool("SYNC_OFF_DAILY_DELTA_CELERY", False)
|
||||
WGER_SETTINGS["USE_RECAPTCHA"] = env.bool("USE_RECAPTCHA", False)
|
||||
WGER_SETTINGS["USE_CELERY"] = env.bool("USE_CELERY", False)
|
||||
WGER_SETTINGS["CACHE_API_EXERCISES_CELERY"] = env.bool("CACHE_API_EXERCISES_CELERY", False)
|
||||
WGER_SETTINGS["CACHE_API_EXERCISES_CELERY_FORCE_UPDATE"] = env.bool("CACHE_API_EXERCISES_CELERY_FORCE_UPDATE", False)
|
||||
|
||||
#
|
||||
# Auth Proxy Authentication
|
||||
|
||||
48
wger/exercises/cache.py
Normal file
48
wger/exercises/cache.py
Normal file
@@ -0,0 +1,48 @@
|
||||
# This file is part of wger Workout Manager.
|
||||
#
|
||||
# wger Workout Manager is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# wger Workout Manager is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
|
||||
# Standard Library
|
||||
from typing import Callable
|
||||
|
||||
# wger
|
||||
from wger.exercises.api.serializers import ExerciseInfoSerializer
|
||||
from wger.exercises.models import Exercise
|
||||
from wger.utils.cache import reset_exercise_api_cache
|
||||
|
||||
|
||||
def cache_exercise(
|
||||
exercise: Exercise, force=False, print_fn: Callable = print, style_fn: Callable = lambda x: x
|
||||
):
|
||||
"""
|
||||
Caches a provided exercise.
|
||||
"""
|
||||
if force:
|
||||
print_fn(f'Force updating cache for exercise {exercise.uuid}')
|
||||
reset_exercise_api_cache(exercise.uuid)
|
||||
else:
|
||||
print_fn(f'Warming cache for exercise {exercise.uuid}')
|
||||
|
||||
serializer = ExerciseInfoSerializer(exercise)
|
||||
serializer.data
|
||||
|
||||
|
||||
def cache_api_exercises(
|
||||
print_fn: Callable,
|
||||
force: bool,
|
||||
style_fn: Callable = lambda x: x,
|
||||
):
|
||||
print_fn('*** Caching API exercises ***')
|
||||
for exercise in Exercise.with_translations.all():
|
||||
cache_exercise(exercise, force, print_fn, style_fn)
|
||||
print_fn(style_fn('Exercises cached!\n'))
|
||||
@@ -16,9 +16,8 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
# wger
|
||||
from wger.exercises.api.serializers import ExerciseInfoSerializer
|
||||
from wger.exercises.cache import cache_exercise
|
||||
from wger.exercises.models import Exercise
|
||||
from wger.utils.cache import reset_exercise_api_cache
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
@@ -48,20 +47,8 @@ class Command(BaseCommand):
|
||||
|
||||
if exercise_id:
|
||||
exercise = Exercise.objects.get(pk=exercise_id)
|
||||
self.handle_cache(exercise, force)
|
||||
cache_exercise(exercise, force, self.stdout.write)
|
||||
return
|
||||
|
||||
for exercise in Exercise.with_translations.all():
|
||||
self.handle_cache(exercise, force)
|
||||
|
||||
def handle_cache(self, exercise: Exercise, force: bool):
|
||||
if force:
|
||||
self.stdout.write(f'Force updating cache for exercise base {exercise.uuid}')
|
||||
else:
|
||||
self.stdout.write(f'Warming cache for exercise base {exercise.uuid}')
|
||||
|
||||
if force:
|
||||
reset_exercise_api_cache(exercise.uuid)
|
||||
|
||||
serializer = ExerciseInfoSerializer(exercise)
|
||||
serializer.data
|
||||
cache_exercise(exercise, force, self.stdout.write)
|
||||
|
||||
@@ -24,6 +24,7 @@ from celery.schedules import crontab
|
||||
|
||||
# wger
|
||||
from wger.celery_configuration import app
|
||||
from wger.exercises.cache import cache_api_exercises
|
||||
from wger.exercises.sync import (
|
||||
download_exercise_images,
|
||||
download_exercise_videos,
|
||||
@@ -70,6 +71,15 @@ def sync_videos_task():
|
||||
download_exercise_videos(logger.info)
|
||||
|
||||
|
||||
@app.task
|
||||
def cache_api_exercises_task():
|
||||
"""
|
||||
Fetches all exercises from database and caches them.
|
||||
"""
|
||||
force = settings.WGER_SETTINGS['CACHE_API_EXERCISES_CELERY_FORCE_UPDATE']
|
||||
cache_api_exercises(logger.info, force)
|
||||
|
||||
|
||||
@app.on_after_finalize.connect
|
||||
def setup_periodic_tasks(sender, **kwargs):
|
||||
if settings.WGER_SETTINGS['SYNC_EXERCISES_CELERY']:
|
||||
@@ -104,3 +114,14 @@ def setup_periodic_tasks(sender, **kwargs):
|
||||
sync_videos_task.s(),
|
||||
name='Sync exercise videos',
|
||||
)
|
||||
|
||||
if settings.WGER_SETTINGS['CACHE_API_EXERCISES_CELERY']:
|
||||
sender.add_periodic_task(
|
||||
crontab(
|
||||
hour=str(random.randint(0, 23)),
|
||||
minute=str(random.randint(0, 59)),
|
||||
day_of_week=str(random.randint(0, 6)),
|
||||
),
|
||||
cache_api_exercises_task.s(),
|
||||
name='Cache API exercises',
|
||||
)
|
||||
|
||||
60
wger/exercises/tests/test_cache.py
Normal file
60
wger/exercises/tests/test_cache.py
Normal file
@@ -0,0 +1,60 @@
|
||||
# Standard Library
|
||||
from unittest.mock import (
|
||||
MagicMock,
|
||||
patch,
|
||||
)
|
||||
|
||||
# wger
|
||||
from wger.core.tests.base_testcase import WgerTestCase
|
||||
from wger.exercises.cache import (
|
||||
cache_api_exercises,
|
||||
cache_exercise,
|
||||
)
|
||||
from wger.exercises.models import Exercise
|
||||
|
||||
|
||||
class TestCacheExercise(WgerTestCase):
|
||||
@patch('wger.exercises.cache.reset_exercise_api_cache')
|
||||
@patch('wger.exercises.cache.ExerciseInfoSerializer')
|
||||
def test_cache_exercise_force_true(self, mock_serializer, mock_reset_cache):
|
||||
exercise = Exercise.objects.first()
|
||||
output = []
|
||||
|
||||
serializer_instance = MagicMock()
|
||||
serializer_instance.data = {'mocked': True}
|
||||
mock_serializer.return_value = serializer_instance
|
||||
|
||||
cache_exercise(exercise, force=True, print_fn=output.append)
|
||||
|
||||
mock_reset_cache.assert_called_once_with(exercise.uuid)
|
||||
mock_serializer.assert_called_once_with(exercise)
|
||||
self.assertTrue(any('Force updating cache' in msg for msg in output))
|
||||
|
||||
@patch('wger.exercises.cache.reset_exercise_api_cache')
|
||||
@patch('wger.exercises.cache.ExerciseInfoSerializer')
|
||||
def test_cache_exercise_force_false(self, mock_serializer, mock_reset_cache):
|
||||
exercise = Exercise.objects.first()
|
||||
output = []
|
||||
|
||||
serializer_instance = MagicMock()
|
||||
serializer_instance.data = {'mocked': True}
|
||||
mock_serializer.return_value = serializer_instance
|
||||
|
||||
cache_exercise(exercise, force=False, print_fn=output.append)
|
||||
|
||||
mock_reset_cache.assert_not_called()
|
||||
mock_serializer.assert_called_once_with(exercise)
|
||||
self.assertTrue(any('Warming cache' in msg for msg in output))
|
||||
|
||||
@patch('wger.exercises.cache.cache_exercise')
|
||||
def test_cache_api_exercises_calls_all(self, mock_cache_exercise):
|
||||
output = []
|
||||
called_exercises = []
|
||||
|
||||
def fake_cache(exercise, force, print_fn, style_fn):
|
||||
called_exercises.append(exercise)
|
||||
|
||||
mock_cache_exercise.side_effect = fake_cache
|
||||
cache_api_exercises(print_fn=output.append, force=True)
|
||||
|
||||
self.assertTrue(len(called_exercises) > 0)
|
||||
@@ -556,6 +556,8 @@ WGER_SETTINGS = {
|
||||
'SYNC_EXERCISE_VIDEOS_CELERY': False,
|
||||
'SYNC_INGREDIENTS_CELERY': False,
|
||||
'SYNC_OFF_DAILY_DELTA_CELERY': False,
|
||||
'CACHE_API_EXERCISES_CELERY': False,
|
||||
'CACHE_API_EXERCISES_CELERY_FORCE_UPDATE': False,
|
||||
'TWITTER': False,
|
||||
'MASTODON': 'https://fosstodon.org/@wger',
|
||||
'USE_CELERY': False,
|
||||
|
||||
Reference in New Issue
Block a user