Skip to content

Commit

Permalink
[Feature #159236196] Cache nutritional plan values
Browse files Browse the repository at this point in the history
  • Loading branch information
Yiga-fred committed Aug 9, 2018
1 parent 18c0791 commit fbabb51
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 46 deletions.
1 change: 1 addition & 0 deletions wger/nutrition/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@
from wger import get_version

VERSION = get_version()
default_app_config = 'wger.nutrition.apps.NutritionPlanValuesConfig'
25 changes: 25 additions & 0 deletions wger/nutrition/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-

# 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

from django.apps import AppConfig


class NutritionPlanValuesConfig(AppConfig):
name = 'wger.nutrition'
verbose_name = "NutritionPlan"

def ready(self):
import wger.nutrition.signals
102 changes: 56 additions & 46 deletions wger/nutrition/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,52 +110,62 @@ def get_nutritional_values(self):
'''
Sums the nutritional info of all items in the plan
'''
use_metric = self.user.userprofile.use_metric
unit = 'kg' if use_metric else 'lb'
result = {'total': {'energy': 0,
'protein': 0,
'carbohydrates': 0,
'carbohydrates_sugar': 0,
'fat': 0,
'fat_saturated': 0,
'fibres': 0,
'sodium': 0},
'percent': {'protein': 0,
'carbohydrates': 0,
'fat': 0},
'per_kg': {'protein': 0,
'carbohydrates': 0,
'fat': 0},
}

# Energy
for meal in self.meal_set.select_related():
values = meal.get_nutritional_values(use_metric=use_metric)
for key in result['total'].keys():
result['total'][key] += values[key]

energy = result['total']['energy']

# In percent
if energy:
for key in result['percent'].keys():
result['percent'][key] = \
result['total'][key] * \
ENERGY_FACTOR[key][unit] / energy * 100

# Per body weight
weight_entry = self.get_closest_weight_entry()
if weight_entry:
for key in result['per_kg'].keys():
result['per_kg'][key] = result['total'][key] / \
weight_entry.weight

# Only 2 decimal places, anything else doesn't make sense
for key in result.keys():
for i in result[key]:
result[key][i] = Decimal(result[key][i]).quantize(TWOPLACES)

return result
# query for the cache
nutritional_plan_values_canonical_form = cache.get(
cache_mapper.get_nutritional_plan_canonical(self.pk)
)
# check if cache exit
if not nutritional_plan_values_canonical_form:

use_metric = self.user.userprofile.use_metric
unit = 'kg' if use_metric else 'lb'
result = {'total': {'energy': 0,
'protein': 0,
'carbohydrates': 0,
'carbohydrates_sugar': 0,
'fat': 0,
'fat_saturated': 0,
'fibres': 0,
'sodium': 0},
'percent': {'protein': 0,
'carbohydrates': 0,
'fat': 0},
'per_kg': {'protein': 0,
'carbohydrates': 0,
'fat': 0},
}

# Energy
for meal in self.meal_set.select_related():
values = meal.get_nutritional_values(use_metric=use_metric)
for key in result['total'].keys():
result['total'][key] += values[key]

energy = result['total']['energy']

# In percent
if energy:
for key in result['percent'].keys():
result['percent'][key] = \
result['total'][key] * \
ENERGY_FACTOR[key][unit] / energy * 100

# Per body weight
weight_entry = self.get_closest_weight_entry()
if weight_entry:
for key in result['per_kg'].keys():
result['per_kg'][key] = result['total'][key] / \
weight_entry.weight

# Only 2 decimal places, anything else doesn't make sense
for key in result.keys():
for i in result[key]:
result[key][i] = Decimal(result[key][i]).quantize(TWOPLACES)
nutritional_plan_values_canonical_form = result
cache.set(cache_mapper.get_nutritional_plan_canonical(self.pk),
nutritional_plan_values_canonical_form
)
return nutritional_plan_values_canonical_form

def get_closest_weight_entry(self):
'''
Expand Down
21 changes: 21 additions & 0 deletions wger/nutrition/signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver
from wger.nutrition.models import NutritionPlan, Meal, MealItem
from django.core.cache import cache
from wger.utils.cache import cache_mapper


@receiver(post_save, sender=NutritionPlan)
@receiver(post_delete, sender=NutritionPlan)
@receiver(post_delete, sender=MealItem)
@receiver(post_save, sender=MealItem)
@receiver(post_save, sender=Meal)
@receiver(post_delete, sender=Meal)
def reset_nutrional_plan_canonical(sender, **kwargs):
'''
Function to send signal
'''
instance_sender = kwargs['instance']
if isinstance(instance_sender, (MealItem, NutritionPlan, Meal)):
cache.delete(cache_mapper.get_nutritional_plan_canonical(
instance_sender.get_owner_object().id))
54 changes: 54 additions & 0 deletions wger/nutrition/tests/test_nutrional_plan_values_canonical.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# 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
from django.core.cache import cache

from wger.nutrition.models import NutritionPlan, Meal, MealItem
from wger.utils.cache import cache_mapper
from wger.core.tests.base_testcase import WorkoutManagerTestCase


class NutritionPlanCacheTestCase(WorkoutManagerTestCase):
'''
Test case for the NutritionalPlan canonical representation
'''
def test_nutritional_plan_canonical_form_cache(self):
'''
Test that the nuttritional plan cache is correctly generated
'''
self.assertFalse(cache.get(cache_mapper.get_nutritional_plan_canonical(1)))
nutritional_plan = NutritionPlan.objects.get(pk=1)
nutritional_plan.get_nutritional_values()
self.assertTrue(cache.get(cache_mapper.get_nutritional_plan_canonical(1)))

def test_nutritional_plan_canonical_form_cache_save(self):
'''
Tests the nutritional_plan values cache when saving a nutritional plan
'''
nutritional_plan = NutritionPlan.objects.get(pk=1)
nutritional_plan.get_nutritional_values()
self.assertTrue(cache.get(cache_mapper.get_nutritional_plan_canonical(1)))

nutritional_plan.save()
self.assertFalse(cache.get(cache_mapper.get_nutritional_plan_canonical(1)))

def test_nutritional_plan_canonical_form_cache_delete(self):
'''
Tests the nutrional plan values cache when deleting
'''
nutritional_plan = NutritionPlan.objects.get(pk=1)
nutritional_plan.get_nutritional_values()
self.assertTrue(cache.get(cache_mapper.get_nutritional_plan_canonical(1)))

nutritional_plan.delete()
self.assertFalse(cache.get(cache_mapper.get_workout_canonical(1)))
8 changes: 8 additions & 0 deletions wger/utils/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ class CacheKeyMapper(object):
EXERCISE_CACHE_KEY_MUSCLE_BG = 'exercise-muscle-bg-{0}'
INGREDIENT_CACHE_KEY = 'ingredient-{0}'
WORKOUT_CANONICAL_REPRESENTATION = 'workout-canonical-representation-{0}'
NUTRITION_PLAN_VALUES_CANONICAL_REPRESENTATION = 'nutritional-plan-values-canonical-\
representation-{0}'
WORKOUT_LOG_LIST = 'workout-log-hash-{0}'

def get_pk(self, param):
Expand Down Expand Up @@ -115,5 +117,11 @@ def get_workout_log_list(self, hash_value):
'''
return self.WORKOUT_LOG_LIST.format(hash_value)

def get_nutritional_plan_canonical(self, param):
'''
Return the nutritional plan canonical representation
'''
return self.NUTRITION_PLAN_VALUES_CANONICAL_REPRESENTATION.format(self.get_pk(param))


cache_mapper = CacheKeyMapper()

0 comments on commit fbabb51

Please sign in to comment.