basic food property calculation
This commit is contained in:
parent
9241638686
commit
6d5592c1be
@ -15,7 +15,7 @@ from .models import (BookmarkletImport, Comment, CookLog, Food, FoodInheritField
|
||||
Recipe, RecipeBook, RecipeBookEntry, RecipeImport, SearchPreference, ShareLink,
|
||||
ShoppingList, ShoppingListEntry, ShoppingListRecipe, Space, Step, Storage,
|
||||
Supermarket, SupermarketCategory, SupermarketCategoryRelation, Sync, SyncLog,
|
||||
TelegramBot, Unit, UserFile, UserPreference, ViewLog, Automation, UserSpace, UnitConversion, NutritionType, FoodNutrition)
|
||||
TelegramBot, Unit, UserFile, UserPreference, ViewLog, Automation, UserSpace, UnitConversion, FoodPropertyType, FoodProperty)
|
||||
|
||||
|
||||
class CustomUserAdmin(UserAdmin):
|
||||
@ -327,18 +327,18 @@ class ShareLinkAdmin(admin.ModelAdmin):
|
||||
admin.site.register(ShareLink, ShareLinkAdmin)
|
||||
|
||||
|
||||
class NutritionTypeAdmin(admin.ModelAdmin):
|
||||
class FoodPropertyTypeAdmin(admin.ModelAdmin):
|
||||
list_display = ('id', 'name')
|
||||
|
||||
|
||||
admin.site.register(NutritionType, NutritionTypeAdmin)
|
||||
admin.site.register(FoodPropertyType, FoodPropertyTypeAdmin)
|
||||
|
||||
|
||||
class FoodNutritionAdmin(admin.ModelAdmin):
|
||||
list_display = ('id', 'food_amount', 'food_unit', 'food', 'nutrition_amount', 'nutrition_type')
|
||||
class FoodPropertyAdmin(admin.ModelAdmin):
|
||||
list_display = ('id', 'food_amount', 'food_unit', 'food', 'property_amount', 'property_type')
|
||||
|
||||
|
||||
admin.site.register(FoodNutrition, FoodNutritionAdmin)
|
||||
admin.site.register(FoodProperty, FoodPropertyAdmin)
|
||||
|
||||
|
||||
class NutritionInformationAdmin(admin.ModelAdmin):
|
||||
|
29
cookbook/helper/food_property_helper.py
Normal file
29
cookbook/helper/food_property_helper.py
Normal file
@ -0,0 +1,29 @@
|
||||
from cookbook.models import FoodPropertyType
|
||||
|
||||
|
||||
def calculate_recipe_properties(recipe):
|
||||
ingredients = []
|
||||
computed_properties = {}
|
||||
|
||||
for s in recipe.steps.all():
|
||||
ingredients += s.ingredients.all()
|
||||
|
||||
for fpt in FoodPropertyType.objects.filter(space=recipe.space).all(): # TODO is this safe or should I require the request context?
|
||||
computed_properties[fpt.id] = {'name': fpt.name, 'food_values': {}, 'total_value': 0}
|
||||
|
||||
# TODO unit conversion support
|
||||
|
||||
for i in ingredients:
|
||||
for p in i.food.foodproperty_set.all():
|
||||
computed_properties[p.property_type.id]['total_value'] += (i.amount / p.food_amount) * p.property_amount
|
||||
computed_properties[p.property_type.id]['food_values'][i.food.id] = add_or_create(computed_properties[p.property_type.id]['food_values'], i.food.id, (i.amount / p.food_amount) * p.property_amount)
|
||||
|
||||
return computed_properties
|
||||
|
||||
# small dict helper to add to existing key or create new, probably a better way of doing this
|
||||
def add_or_create(d, key, value):
|
||||
if key in d:
|
||||
d[key] += value
|
||||
else:
|
||||
d[key] = value
|
||||
return d
|
@ -0,0 +1,73 @@
|
||||
# Generated by Django 4.1.7 on 2023-03-25 05:48
|
||||
|
||||
import cookbook.models
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django_prometheus.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('cookbook', '0188_space_no_sharing_limit'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='unit',
|
||||
name='base_unit',
|
||||
field=models.TextField(blank=True, default=None, max_length=256, null=True),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='UnitConversion',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('base_amount', models.DecimalField(decimal_places=16, default=0, max_digits=32)),
|
||||
('converted_amount', models.DecimalField(decimal_places=16, default=0, max_digits=32)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('base_unit', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='unit_conversion_base_relation', to='cookbook.unit')),
|
||||
('converted_unit', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='unit_conversion_converted_relation', to='cookbook.unit')),
|
||||
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
|
||||
('food', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='cookbook.food')),
|
||||
('space', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.space')),
|
||||
],
|
||||
bases=(django_prometheus.models.ExportModelOperationsMixin('unit_conversion'), models.Model, cookbook.models.PermissionModelMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='FoodPropertyType',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=128)),
|
||||
('unit', models.CharField(blank=True, max_length=64, null=True)),
|
||||
('icon', models.CharField(blank=True, max_length=16, null=True)),
|
||||
('description', models.CharField(blank=True, max_length=512, null=True)),
|
||||
('category', models.CharField(choices=[('NUTRITION', 'Nutrition'), ('ALLERGEN', 'Allergen'), ('PRICE', 'PRICE')], max_length=64)),
|
||||
('space', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.space')),
|
||||
],
|
||||
bases=(models.Model, cookbook.models.PermissionModelMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='FoodProperty',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('food_amount', models.DecimalField(decimal_places=2, default=0, max_digits=32)),
|
||||
('nutrition_amount', models.DecimalField(decimal_places=4, default=0, max_digits=32)),
|
||||
('food', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.food')),
|
||||
('food_unit', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.unit')),
|
||||
('nutrition_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='cookbook.foodpropertytype')),
|
||||
('space', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.space')),
|
||||
],
|
||||
bases=(models.Model, cookbook.models.PermissionModelMixin),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='foodpropertytype',
|
||||
constraint=models.UniqueConstraint(fields=('space', 'name'), name='nutrition_type_unique_name_per_space'),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='foodproperty',
|
||||
constraint=models.UniqueConstraint(fields=('food', 'nutrition_type', 'space'), name='food_nutrition_unique_per_space'),
|
||||
),
|
||||
]
|
@ -1,34 +0,0 @@
|
||||
# Generated by Django 4.1.7 on 2023-02-24 19:47
|
||||
|
||||
import cookbook.models
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django_prometheus.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('cookbook', '0188_space_no_sharing_limit'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='UnitConversion',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('base_amount', models.DecimalField(decimal_places=16, default=0, max_digits=32)),
|
||||
('converted_amount', models.DecimalField(decimal_places=16, default=0, max_digits=32)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('base_unit', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='base_unit', to='cookbook.unit')),
|
||||
('converted_unit', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='converted_unit', to='cookbook.unit')),
|
||||
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
|
||||
('food', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='cookbook.food')),
|
||||
('space', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.space')),
|
||||
],
|
||||
bases=(django_prometheus.models.ExportModelOperationsMixin('unit_conversion'), models.Model, cookbook.models.PermissionModelMixin),
|
||||
),
|
||||
]
|
@ -1,40 +0,0 @@
|
||||
# Generated by Django 4.1.7 on 2023-02-24 20:15
|
||||
|
||||
import cookbook.models
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cookbook', '0189_unitconversion'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='NutritionType',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=128)),
|
||||
('unit', models.CharField(blank=True, max_length=64, null=True)),
|
||||
('icon', models.CharField(blank=True, max_length=16, null=True)),
|
||||
('description', models.CharField(blank=True, max_length=512, null=True)),
|
||||
('space', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.space')),
|
||||
],
|
||||
bases=(models.Model, cookbook.models.PermissionModelMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='FoodNutrition',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('food_amount', models.DecimalField(decimal_places=2, default=0, max_digits=32)),
|
||||
('nutrition_amount', models.DecimalField(decimal_places=4, default=0, max_digits=32)),
|
||||
('food', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.food')),
|
||||
('food_unit', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.unit')),
|
||||
('nutrition_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='cookbook.nutritiontype')),
|
||||
('space', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.space')),
|
||||
],
|
||||
bases=(models.Model, cookbook.models.PermissionModelMixin),
|
||||
),
|
||||
]
|
@ -0,0 +1,44 @@
|
||||
# Generated by Django 4.1.7 on 2023-03-25 06:04
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cookbook', '0189_unit_base_unit_unitconversion_foodpropertytype_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveConstraint(
|
||||
model_name='foodproperty',
|
||||
name='food_nutrition_unique_per_space',
|
||||
),
|
||||
migrations.RemoveConstraint(
|
||||
model_name='foodpropertytype',
|
||||
name='nutrition_type_unique_name_per_space',
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name='foodproperty',
|
||||
old_name='nutrition_amount',
|
||||
new_name='property_amount',
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name='foodproperty',
|
||||
old_name='nutrition_type',
|
||||
new_name='property_type',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='foodpropertytype',
|
||||
name='category',
|
||||
field=models.CharField(blank=True, choices=[('NUTRITION', 'Nutrition'), ('ALLERGEN', 'Allergen'), ('PRICE', 'PRICE')], max_length=64, null=True),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='foodproperty',
|
||||
constraint=models.UniqueConstraint(fields=('food', 'property_type', 'space'), name='food_property_unique_per_space'),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='foodpropertytype',
|
||||
constraint=models.UniqueConstraint(fields=('space', 'name'), name='food_property_type_unique_name_per_space'),
|
||||
),
|
||||
]
|
@ -1,21 +0,0 @@
|
||||
# Generated by Django 4.1.7 on 2023-02-24 21:15
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cookbook', '0190_nutritiontype_foodnutrition'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddConstraint(
|
||||
model_name='foodnutrition',
|
||||
constraint=models.UniqueConstraint(fields=('food', 'nutrition_type', 'space'), name='food_nutrition_unique_per_space'),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='nutritiontype',
|
||||
constraint=models.UniqueConstraint(fields=('space', 'name'), name='nutrition_type_unique_name_per_space'),
|
||||
),
|
||||
]
|
@ -1,24 +0,0 @@
|
||||
# Generated by Django 4.1.7 on 2023-02-26 06:30
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cookbook', '0191_foodnutrition_food_nutrition_unique_per_space_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='unitconversion',
|
||||
name='base_unit',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='unit_conversion_base_unit', to='cookbook.unit'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='unitconversion',
|
||||
name='converted_unit',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='unit_conversion_converted_unit', to='cookbook.unit'),
|
||||
),
|
||||
]
|
@ -1,24 +0,0 @@
|
||||
# Generated by Django 4.1.7 on 2023-02-26 07:03
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cookbook', '0192_alter_unitconversion_base_unit_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='unitconversion',
|
||||
name='base_unit',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='unit_conversion_base_relation', to='cookbook.unit'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='unitconversion',
|
||||
name='converted_unit',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='unit_conversion_converted_relation', to='cookbook.unit'),
|
||||
),
|
||||
]
|
@ -1,18 +0,0 @@
|
||||
# Generated by Django 4.1.7 on 2023-03-15 14:57
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cookbook', '0193_alter_unitconversion_base_unit_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='unit',
|
||||
name='base_unit',
|
||||
field=models.TextField(blank=True, default=None, max_length=256, null=True),
|
||||
),
|
||||
]
|
@ -683,8 +683,6 @@ class Ingredient(ExportModelOperationsMixin('ingredient'), models.Model, Permiss
|
||||
order = models.IntegerField(default=0)
|
||||
original_text = models.CharField(max_length=512, null=True, blank=True, default=None)
|
||||
|
||||
original_text = models.CharField(max_length=512, null=True, blank=True, default=None)
|
||||
|
||||
space = models.ForeignKey(Space, on_delete=models.CASCADE)
|
||||
objects = ScopedManager(space='space')
|
||||
|
||||
@ -740,11 +738,19 @@ class Step(ExportModelOperationsMixin('step'), models.Model, PermissionModelMixi
|
||||
indexes = (GinIndex(fields=["search_vector"]),)
|
||||
|
||||
|
||||
class NutritionType(models.Model, PermissionModelMixin):
|
||||
class FoodPropertyType(models.Model, PermissionModelMixin):
|
||||
NUTRITION = 'NUTRITION'
|
||||
ALLERGEN = 'ALLERGEN'
|
||||
PRICE = 'PRICE'
|
||||
|
||||
name = models.CharField(max_length=128)
|
||||
unit = models.CharField(max_length=64, blank=True, null=True)
|
||||
icon = models.CharField(max_length=16, blank=True, null=True)
|
||||
description = models.CharField(max_length=512, blank=True, null=True)
|
||||
category = models.CharField(max_length=64, choices=((NUTRITION, _('Nutrition')), (ALLERGEN, _('Allergen')), (PRICE, _('PRICE'))), null=True, blank=True)
|
||||
|
||||
# TODO show if empty property?
|
||||
# TODO formatting property?
|
||||
|
||||
space = models.ForeignKey(Space, on_delete=models.CASCADE)
|
||||
objects = ScopedManager(space='space')
|
||||
@ -754,26 +760,26 @@ class NutritionType(models.Model, PermissionModelMixin):
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(fields=['space', 'name'], name='nutrition_type_unique_name_per_space')
|
||||
models.UniqueConstraint(fields=['space', 'name'], name='food_property_type_unique_name_per_space')
|
||||
]
|
||||
|
||||
|
||||
class FoodNutrition(models.Model, PermissionModelMixin):
|
||||
class FoodProperty(models.Model, PermissionModelMixin):
|
||||
food_amount = models.DecimalField(default=0, decimal_places=2, max_digits=32)
|
||||
food_unit = models.ForeignKey(Unit, on_delete=models.CASCADE)
|
||||
food = models.ForeignKey(Food, on_delete=models.CASCADE)
|
||||
nutrition_amount = models.DecimalField(default=0, decimal_places=4, max_digits=32)
|
||||
nutrition_type = models.ForeignKey(NutritionType, on_delete=models.PROTECT)
|
||||
property_amount = models.DecimalField(default=0, decimal_places=4, max_digits=32)
|
||||
property_type = models.ForeignKey(FoodPropertyType, on_delete=models.PROTECT)
|
||||
|
||||
space = models.ForeignKey(Space, on_delete=models.CASCADE)
|
||||
objects = ScopedManager(space='space')
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.food_amount} {self.food_unit} {self.food}: {self.nutrition_amount} {self.nutrition_type.unit} {self.nutrition_type.name}'
|
||||
return f'{self.food_amount} {self.food_unit} {self.food}: {self.property_amount} {self.property_type.unit} {self.property_type.name}'
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(fields=['food', 'nutrition_type', 'space'], name='food_nutrition_unique_per_space')
|
||||
models.UniqueConstraint(fields=['food', 'property_type', 'space'], name='food_property_unique_per_space')
|
||||
]
|
||||
|
||||
|
||||
|
@ -31,8 +31,8 @@ from cookbook.models import (Automation, BookmarkletImport, Comment, CookLog, Cu
|
||||
RecipeBookEntry, RecipeImport, ShareLink, ShoppingList,
|
||||
ShoppingListEntry, ShoppingListRecipe, Space, Step, Storage,
|
||||
Supermarket, SupermarketCategory, SupermarketCategoryRelation, Sync,
|
||||
SyncLog, Unit, UserFile, UserPreference, UserSpace, ViewLog, UnitConversion, FoodNutrition,
|
||||
NutritionType)
|
||||
SyncLog, Unit, UserFile, UserPreference, UserSpace, ViewLog, UnitConversion, FoodProperty,
|
||||
FoodPropertyType)
|
||||
from cookbook.templatetags.custom_tags import markdown
|
||||
from recipes.settings import AWS_ENABLED, MEDIA_URL
|
||||
|
||||
@ -660,12 +660,12 @@ class IngredientSimpleSerializer(WritableNestedModelSerializer):
|
||||
if ingredient.food:
|
||||
for fn in ingredient.food.foodnutrition_set.all():
|
||||
if fn.food_unit == ingredient.unit:
|
||||
nutritions[fn.nutrition_type.id] = ingredient.amount / fn.food_amount * fn.nutrition_amount
|
||||
nutritions[fn.property_type.id] = ingredient.amount / fn.food_amount * fn.property_amount
|
||||
else:
|
||||
conversions = self.get_conversions(ingredient)
|
||||
for c in conversions:
|
||||
if fn.food_unit.id == c['unit']['id']:
|
||||
nutritions[fn.nutrition_type.id] = c['amount'] / fn.food_amount * fn.nutrition_amount
|
||||
nutritions[fn.property_type.id] = c['amount'] / fn.food_amount * fn.property_amount
|
||||
|
||||
return nutritions
|
||||
|
||||
@ -758,7 +758,7 @@ class NutritionTypeSerializer(serializers.ModelSerializer):
|
||||
return super().create(validated_data)
|
||||
|
||||
class Meta:
|
||||
model = NutritionType
|
||||
model = FoodPropertyType
|
||||
fields = ('id', 'name', 'icon', 'unit', 'description')
|
||||
|
||||
|
||||
|
42
cookbook/tests/other/test_food_property.py
Normal file
42
cookbook/tests/other/test_food_property.py
Normal file
@ -0,0 +1,42 @@
|
||||
from django.contrib import auth
|
||||
from django_scopes import scopes_disabled
|
||||
|
||||
from cookbook.helper.food_property_helper import calculate_recipe_properties
|
||||
from cookbook.helper.unit_conversion_helper import get_conversions
|
||||
from cookbook.models import Unit, Food, Ingredient, UnitConversion, FoodPropertyType, FoodProperty, Recipe, Step
|
||||
|
||||
|
||||
def test_food_property(space_1, u1_s1):
|
||||
with scopes_disabled():
|
||||
unit_gram = Unit.objects.create(name='gram', base_unit='g', 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)
|
||||
unit_fantasy = Unit.objects.create(name='Fantasy Unit', base_unit='', space=space_1)
|
||||
|
||||
food_1 = Food.objects.create(name='food_1', space=space_1)
|
||||
food_2 = Food.objects.create(name='food_2', space=space_1)
|
||||
|
||||
property_fat = FoodPropertyType.objects.create(name='property_fat', space=space_1)
|
||||
property_calories = FoodPropertyType.objects.create(name='property_calories', space=space_1)
|
||||
property_nuts = FoodPropertyType.objects.create(name='property_nuts', space=space_1)
|
||||
property_price = FoodPropertyType.objects.create(name='property_price', space=space_1)
|
||||
|
||||
food_1_property_fat = FoodProperty.objects.create(food_amount=100, food_unit=unit_gram, food=food_1, property_amount=50, property_type=property_fat, space=space_1)
|
||||
food_1_property_nuts = FoodProperty.objects.create(food_amount=100, food_unit=unit_gram, food=food_1, property_amount=1, property_type=property_nuts, space=space_1)
|
||||
food_1_property_price = FoodProperty.objects.create(food_amount=100, food_unit=unit_gram, food=food_1, property_amount=7.50, property_type=property_price, space=space_1)
|
||||
|
||||
food_2_property_fat = FoodProperty.objects.create(food_amount=100, food_unit=unit_gram, food=food_2, property_amount=25, property_type=property_fat, space=space_1)
|
||||
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)
|
||||
|
||||
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)
|
||||
step_1.ingredients.create(amount=500, unit=unit_gram, food=food_1, space=space_1)
|
||||
step_1.ingredients.create(amount=1000, unit=unit_gram, food=food_2, space=space_1)
|
||||
recipe_1.steps.add(step_1)
|
||||
recipe_1.save()
|
||||
|
||||
property_values = calculate_recipe_properties(recipe_1)
|
||||
for p in property_values:
|
||||
print(p)
|
@ -12,7 +12,7 @@ from recipes.version import VERSION_NUMBER
|
||||
from .models import (Automation, Comment, CustomFilter, Food, InviteLink, Keyword, MealPlan, Recipe,
|
||||
RecipeBook, RecipeBookEntry, RecipeImport, ShoppingList, Step, Storage,
|
||||
Supermarket, SupermarketCategory, Sync, SyncLog, Unit, UserFile,
|
||||
get_model_name, UserSpace, Space, NutritionType, UnitConversion)
|
||||
get_model_name, UserSpace, Space, FoodPropertyType, UnitConversion)
|
||||
from .views import api, data, delete, edit, import_export, lists, new, telegram, views
|
||||
from .views.api import CustomAuthToken
|
||||
|
||||
@ -191,7 +191,7 @@ for m in generic_models:
|
||||
)
|
||||
)
|
||||
|
||||
vue_models = [Food, Keyword, Unit, Supermarket, SupermarketCategory, Automation, UserFile, Step, CustomFilter, UnitConversion, NutritionType]
|
||||
vue_models = [Food, Keyword, Unit, Supermarket, SupermarketCategory, Automation, UserFile, Step, CustomFilter, UnitConversion, FoodPropertyType]
|
||||
for m in vue_models:
|
||||
py_name = get_model_name(m)
|
||||
url_name = py_name.replace('_', '-')
|
||||
|
@ -68,7 +68,7 @@ from cookbook.models import (Automation, BookmarkletImport, CookLog, CustomFilte
|
||||
MealType, Recipe, RecipeBook, RecipeBookEntry, ShareLink, ShoppingList,
|
||||
ShoppingListEntry, ShoppingListRecipe, Space, Step, Storage,
|
||||
Supermarket, SupermarketCategory, SupermarketCategoryRelation, Sync,
|
||||
SyncLog, Unit, UserFile, UserPreference, UserSpace, ViewLog, UnitConversion, NutritionType)
|
||||
SyncLog, Unit, UserFile, UserPreference, UserSpace, ViewLog, UnitConversion, FoodPropertyType)
|
||||
from cookbook.provider.dropbox import Dropbox
|
||||
from cookbook.provider.local import Local
|
||||
from cookbook.provider.nextcloud import Nextcloud
|
||||
@ -820,10 +820,10 @@ class RecipeViewSet(viewsets.ModelViewSet):
|
||||
'steps__ingredients__food__onhand_users',
|
||||
'steps__ingredients__food__substitute',
|
||||
'steps__ingredients__food__child_inherit_fields',
|
||||
'steps__ingredients__food__foodnutrition_set',
|
||||
'steps__ingredients__food__foodnutrition_set__food',
|
||||
'steps__ingredients__food__foodnutrition_set__food_unit',
|
||||
'steps__ingredients__food__foodnutrition_set__nutrition_type',
|
||||
# 'steps__ingredients__food__foodnutrition_set',
|
||||
# 'steps__ingredients__food__foodnutrition_set__food',
|
||||
# 'steps__ingredients__food__foodnutrition_set__food_unit',
|
||||
# 'steps__ingredients__food__foodnutrition_set__nutrition_type',
|
||||
'steps__ingredients__unit',
|
||||
'steps__ingredients__unit__unit_conversion_base_relation',
|
||||
'steps__ingredients__unit__unit_conversion_base_relation__base_unit',
|
||||
@ -970,7 +970,7 @@ class UnitConversionViewSet(viewsets.ModelViewSet):
|
||||
|
||||
|
||||
class NutritionTypeViewSet(viewsets.ModelViewSet):
|
||||
queryset = NutritionType.objects
|
||||
queryset = FoodPropertyType.objects
|
||||
serializer_class = NutritionTypeSerializer
|
||||
permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
|
||||
|
||||
|
@ -246,15 +246,15 @@ def unit_conversion(request):
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def nutrition_type(request):
|
||||
def food_property_type(request):
|
||||
# model-name is the models.js name of the model, probably ALL-CAPS
|
||||
return render(
|
||||
request,
|
||||
'generic/model_template.html',
|
||||
{
|
||||
"title": _("Unit Conversions"),
|
||||
"title": _("Food Property Types"),
|
||||
"config": {
|
||||
'model': "NUTRITION_TYPE", # *REQUIRED* name of the model in models.js
|
||||
'model': "FOOD_PROPERTY_TYPE", # *REQUIRED* name of the model in models.js
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -622,9 +622,9 @@ export class Models {
|
||||
},
|
||||
}
|
||||
|
||||
static NUTRITION_TYPE = {
|
||||
name: "Nutrition Type",
|
||||
apiName: "NutritionType",
|
||||
static FOOD_PROPERTY_TYPE = {
|
||||
name: "Food Property Type",
|
||||
apiName: "FoodPropertyType",
|
||||
paginated: false,
|
||||
list: {
|
||||
header_component: {
|
||||
|
Loading…
Reference in New Issue
Block a user