improvements to property calculation
This commit is contained in:
@ -2,8 +2,10 @@ class CacheHelper:
|
||||
space = None
|
||||
|
||||
BASE_UNITS_CACHE_KEY = None
|
||||
PROPERTY_TYPE_CACHE_KEY = None
|
||||
|
||||
def __init__(self, space):
|
||||
self.space = space
|
||||
|
||||
self.BASE_UNITS_CACHE_KEY = f'SPACE_{space.id}_BASE_UNITS'
|
||||
self.PROPERTY_TYPE_CACHE_KEY = f'SPACE_{space.id}_PROPERTY_TYPES'
|
||||
|
@ -1,3 +1,6 @@
|
||||
from django.core.cache import caches
|
||||
|
||||
from cookbook.helper.cache_helper import CacheHelper
|
||||
from cookbook.helper.unit_conversion_helper import UnitConversionHelper
|
||||
from cookbook.models import PropertyType, Unit, Food, FoodProperty, Recipe, Step
|
||||
|
||||
@ -20,15 +23,18 @@ class FoodPropertyHelper:
|
||||
"""
|
||||
ingredients = []
|
||||
computed_properties = {}
|
||||
property_types = PropertyType.objects.filter(space=self.space).all()
|
||||
|
||||
for s in recipe.steps.all():
|
||||
ingredients += s.ingredients.all()
|
||||
|
||||
for fpt in property_types: # TODO is this safe or should I require the request context?
|
||||
computed_properties[fpt.id] = {'id': fpt.id, 'name': fpt.name, 'icon': fpt.icon, 'description': fpt.description, 'unit': fpt.unit, 'food_values': {}, 'total_value': 0, 'missing_value': False}
|
||||
property_types = caches['default'].get(CacheHelper(self.space).PROPERTY_TYPE_CACHE_KEY, None)
|
||||
|
||||
# TODO unit conversion support
|
||||
if not property_types:
|
||||
property_types = PropertyType.objects.filter(space=self.space).all()
|
||||
caches['default'].set(CacheHelper(self.space).PROPERTY_TYPE_CACHE_KEY, property_types, 60 * 60) # cache is cleared on property type save signal so long duration is fine
|
||||
|
||||
for fpt in property_types:
|
||||
computed_properties[fpt.id] = {'id': fpt.id, 'name': fpt.name, 'icon': fpt.icon, 'description': fpt.description, 'unit': fpt.unit, 'food_values': {}, 'total_value': 0, 'missing_value': False}
|
||||
|
||||
uch = UnitConversionHelper(self.space)
|
||||
|
||||
|
@ -14,7 +14,7 @@ from cookbook.helper.cache_helper import CacheHelper
|
||||
from cookbook.helper.shopping_helper import RecipeShoppingEditor
|
||||
from cookbook.managers import DICTIONARY
|
||||
from cookbook.models import (Food, FoodInheritField, Ingredient, MealPlan, Recipe,
|
||||
ShoppingListEntry, Step, UserPreference, SearchPreference, SearchFields, Unit)
|
||||
ShoppingListEntry, Step, UserPreference, SearchPreference, SearchFields, Unit, PropertyType)
|
||||
|
||||
SQLITE = True
|
||||
if settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2',
|
||||
@ -154,6 +154,12 @@ def auto_add_shopping(sender, instance=None, created=False, weak=False, **kwargs
|
||||
|
||||
|
||||
@receiver(post_save, sender=Unit)
|
||||
def create_search_preference(sender, instance=None, created=False, **kwargs):
|
||||
def clear_unit_cache(sender, instance=None, created=False, **kwargs):
|
||||
if instance:
|
||||
caches['default'].delete(CacheHelper(instance.space).BASE_UNITS_CACHE_KEY)
|
||||
|
||||
|
||||
@receiver(post_save, sender=PropertyType)
|
||||
def clear_property_type_cache(sender, instance=None, created=False, **kwargs):
|
||||
if instance:
|
||||
caches['default'].delete(CacheHelper(instance.space).PROPERTY_TYPE_CACHE_KEY)
|
||||
|
@ -1,13 +1,17 @@
|
||||
from django.contrib import auth
|
||||
from django.core.cache import caches
|
||||
from django_scopes import scopes_disabled
|
||||
from decimal import Decimal
|
||||
|
||||
from cookbook.helper.cache_helper import CacheHelper
|
||||
from cookbook.helper.property_helper import FoodPropertyHelper
|
||||
from cookbook.models import Unit, Food, PropertyType, FoodProperty, Recipe, Step
|
||||
from cookbook.models import Unit, Food, PropertyType, FoodProperty, Recipe, Step, UnitConversion
|
||||
|
||||
|
||||
def test_food_property(space_1, u1_s1):
|
||||
def test_food_property(space_1, space_2, u1_s1):
|
||||
with scopes_disabled():
|
||||
unit_gram = Unit.objects.create(name='gram', base_unit='g', space=space_1)
|
||||
unit_kg = Unit.objects.create(name='kg', base_unit='kg', space=space_1)
|
||||
unit_pcs = Unit.objects.create(name='pcs', base_unit='', space=space_1)
|
||||
unit_floz1 = Unit.objects.create(name='fl. oz 1', base_unit='imperial_fluid_ounce', space=space_1) # US and UK use different volume systems (US vs imperial)
|
||||
unit_floz2 = Unit.objects.create(name='fl. oz 2', base_unit='fluid_ounce', space=space_1)
|
||||
@ -29,6 +33,7 @@ def test_food_property(space_1, u1_s1):
|
||||
food_2_property_nuts = FoodProperty.objects.create(food_amount=100, food_unit=unit_gram, food=food_2, property_amount=0, property_type=property_nuts, space=space_1)
|
||||
food_2_property_price = FoodProperty.objects.create(food_amount=100, food_unit=unit_gram, food=food_2, property_amount=2.50, property_type=property_price, space=space_1)
|
||||
|
||||
print('\n----------- TEST PROPERTY - PROPERTY CALCULATION MULTI STEP IDENTICAL UNIT ---------------')
|
||||
recipe_1 = Recipe.objects.create(name='recipe_1', waiting_time=0, working_time=0, space=space_1, created_by=auth.get_user(u1_s1))
|
||||
|
||||
step_1 = Step.objects.create(instruction='instruction_step_1', space=space_1)
|
||||
@ -43,10 +48,67 @@ def test_food_property(space_1, u1_s1):
|
||||
property_values = FoodPropertyHelper(space_1).calculate_recipe_properties(recipe_1)
|
||||
|
||||
assert property_values[property_fat.id]['name'] == property_fat.name
|
||||
assert property_values[property_fat.id]['total_value'] == 525 # TODO manually validate those numbers
|
||||
assert property_values[property_fat.id]['food_values'][food_1.id] == 275 # TODO manually validate those numbers
|
||||
assert property_values[property_fat.id]['food_values'][food_2.id] == 250 # TODO manually validate those numbers
|
||||
print(property_values)
|
||||
# TODO more property tests
|
||||
assert abs(property_values[property_fat.id]['total_value'] - Decimal(525)) < 0.0001
|
||||
assert abs(property_values[property_fat.id]['food_values'][food_1.id]['value'] - Decimal(275)) < 0.0001
|
||||
assert abs(property_values[property_fat.id]['food_values'][food_2.id]['value'] - Decimal(250)) < 0.0001
|
||||
|
||||
# TODO test space separation
|
||||
print('\n----------- TEST PROPERTY - PROPERTY CALCULATION NO POSSIBLE CONVERSION ---------------')
|
||||
recipe_2 = Recipe.objects.create(name='recipe_2', waiting_time=0, working_time=0, space=space_1, created_by=auth.get_user(u1_s1))
|
||||
|
||||
step_1 = Step.objects.create(instruction='instruction_step_1', space=space_1)
|
||||
step_1.ingredients.create(amount=5, unit=unit_pcs, food=food_1, space=space_1)
|
||||
step_1.ingredients.create(amount=10, unit=unit_pcs, food=food_2, space=space_1)
|
||||
recipe_2.steps.add(step_1)
|
||||
|
||||
property_values = FoodPropertyHelper(space_1).calculate_recipe_properties(recipe_2)
|
||||
|
||||
assert property_values[property_fat.id]['name'] == property_fat.name
|
||||
assert abs(property_values[property_fat.id]['total_value']) < 0.0001
|
||||
assert abs(property_values[property_fat.id]['food_values'][food_1.id]['value']) < 0.0001
|
||||
|
||||
print('\n----------- TEST PROPERTY - PROPERTY CALCULATION UNIT CONVERSION ---------------')
|
||||
uc1 = UnitConversion.objects.create(
|
||||
base_amount=100,
|
||||
base_unit=unit_gram,
|
||||
converted_amount=1,
|
||||
converted_unit=unit_pcs,
|
||||
space=space_1,
|
||||
created_by=auth.get_user(u1_s1),
|
||||
)
|
||||
|
||||
property_values = FoodPropertyHelper(space_1).calculate_recipe_properties(recipe_2)
|
||||
|
||||
assert property_values[property_fat.id]['name'] == property_fat.name
|
||||
assert abs(property_values[property_fat.id]['total_value'] - Decimal(500)) < 0.0001
|
||||
assert abs(property_values[property_fat.id]['food_values'][food_1.id]['value'] - Decimal(250)) < 0.0001
|
||||
assert abs(property_values[property_fat.id]['food_values'][food_2.id]['value'] - Decimal(250)) < 0.0001
|
||||
|
||||
print('\n----------- TEST PROPERTY - PROPERTY CALCULATION UNIT CONVERSION MULTIPLE ---------------')
|
||||
|
||||
uc1.delete()
|
||||
uc1 = UnitConversion.objects.create(
|
||||
base_amount=0.1,
|
||||
base_unit=unit_kg,
|
||||
converted_amount=1,
|
||||
converted_unit=unit_pcs,
|
||||
space=space_1,
|
||||
created_by=auth.get_user(u1_s1),
|
||||
)
|
||||
|
||||
property_values = FoodPropertyHelper(space_1).calculate_recipe_properties(recipe_2)
|
||||
|
||||
assert property_values[property_fat.id]['name'] == property_fat.name
|
||||
assert abs(property_values[property_fat.id]['total_value'] - Decimal(500)) < 0.0001
|
||||
assert abs(property_values[property_fat.id]['food_values'][food_1.id]['value'] - Decimal(250)) < 0.0001
|
||||
assert abs(property_values[property_fat.id]['food_values'][food_2.id]['value'] - Decimal(250)) < 0.0001
|
||||
|
||||
print('\n----------- TEST PROPERTY - SPACE SEPARATION ---------------')
|
||||
|
||||
property_fat.space = space_2
|
||||
property_fat.save()
|
||||
|
||||
caches['default'].delete(CacheHelper(space_1).PROPERTY_TYPE_CACHE_KEY) # clear cache as objects won't change space in reality
|
||||
|
||||
property_values = FoodPropertyHelper(space_1).calculate_recipe_properties(recipe_2)
|
||||
|
||||
assert property_fat.id not in property_values
|
||||
|
@ -138,6 +138,10 @@ def test_unit_conversions(space_1, space_2, u1_s1):
|
||||
assert len(uch.get_conversions(ingredient_food_2_pcs)) == 1
|
||||
print(uch.get_conversions(ingredient_food_2_pcs))
|
||||
|
||||
print('\n----------- TEST CUSTOM CONVERSION - CONVERT MULTI STEP ---------------')
|
||||
|
||||
# TODO add test for multi step conversion ... do I even do or want to support this ?
|
||||
|
||||
print('\n----------- TEST CUSTOM CONVERSION - REVERSE CONVERSION ---------------')
|
||||
uc2 = UnitConversion.objects.create(
|
||||
base_amount=200,
|
||||
|
Reference in New Issue
Block a user