user nutrition types + ingredient nutrtion calculation
This commit is contained in:
parent
5651beffb2
commit
a2b987352f
@ -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)
|
||||
TelegramBot, Unit, UserFile, UserPreference, ViewLog, Automation, UserSpace, UnitConversion, NutritionType, FoodNutrition)
|
||||
|
||||
|
||||
class CustomUserAdmin(UserAdmin):
|
||||
@ -327,6 +327,20 @@ class ShareLinkAdmin(admin.ModelAdmin):
|
||||
admin.site.register(ShareLink, ShareLinkAdmin)
|
||||
|
||||
|
||||
class NutritionTypeAdmin(admin.ModelAdmin):
|
||||
list_display = ('id', 'name')
|
||||
|
||||
|
||||
admin.site.register(NutritionType, NutritionTypeAdmin)
|
||||
|
||||
|
||||
class FoodNutritionAdmin(admin.ModelAdmin):
|
||||
list_display = ('id', 'food_amount', 'food_unit', 'food', 'nutrition_amount', 'nutrition_type')
|
||||
|
||||
|
||||
admin.site.register(FoodNutrition, FoodNutritionAdmin)
|
||||
|
||||
|
||||
class NutritionInformationAdmin(admin.ModelAdmin):
|
||||
list_display = ('id',)
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Generated by Django 4.1.4 on 2023-01-09 16:41
|
||||
# Generated by Django 4.1.7 on 2023-02-24 19:47
|
||||
|
||||
import cookbook.models
|
||||
from django.conf import settings
|
||||
@ -11,7 +11,7 @@ class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('cookbook', '0186_automation_order_alter_automation_type'),
|
||||
('cookbook', '0188_space_no_sharing_limit'),
|
||||
]
|
||||
|
||||
operations = [
|
40
cookbook/migrations/0190_nutritiontype_foodnutrition.py
Normal file
40
cookbook/migrations/0190_nutritiontype_foodnutrition.py
Normal file
@ -0,0 +1,40 @@
|
||||
# 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),
|
||||
),
|
||||
]
|
@ -739,6 +739,33 @@ class Step(ExportModelOperationsMixin('step'), models.Model, PermissionModelMixi
|
||||
indexes = (GinIndex(fields=["search_vector"]),)
|
||||
|
||||
|
||||
class NutritionType(models.Model, PermissionModelMixin):
|
||||
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)
|
||||
|
||||
space = models.ForeignKey(Space, on_delete=models.CASCADE)
|
||||
objects = ScopedManager(space='space')
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.name}'
|
||||
|
||||
|
||||
class FoodNutrition(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)
|
||||
|
||||
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}'
|
||||
|
||||
|
||||
class NutritionInformation(models.Model, PermissionModelMixin):
|
||||
fats = models.DecimalField(default=0, decimal_places=16, max_digits=32)
|
||||
carbohydrates = models.DecimalField(
|
||||
@ -755,14 +782,6 @@ class NutritionInformation(models.Model, PermissionModelMixin):
|
||||
return f'Nutrition {self.pk}'
|
||||
|
||||
|
||||
# class NutritionType(models.Model, PermissionModelMixin):
|
||||
# name = models.CharField(max_length=128)
|
||||
# icon = models.CharField(max_length=16, blank=True, null=True)
|
||||
# description = models.CharField(max_length=512, blank=True, null=True)
|
||||
#
|
||||
# space = models.ForeignKey(Space, on_delete=models.CASCADE)
|
||||
# objects = ScopedManager(space='space')
|
||||
|
||||
class RecipeManager(models.Manager.from_queryset(models.QuerySet)):
|
||||
def get_queryset(self):
|
||||
return super(RecipeManager, self).get_queryset().annotate(rating=Avg('cooklog__rating')).annotate(last_cooked=Max('cooklog__created_at'))
|
||||
|
@ -29,7 +29,7 @@ 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)
|
||||
SyncLog, Unit, UserFile, UserPreference, UserSpace, ViewLog, UnitConversion, FoodNutrition, NutritionType)
|
||||
from cookbook.templatetags.custom_tags import markdown
|
||||
from recipes.settings import AWS_ENABLED, MEDIA_URL
|
||||
|
||||
@ -619,6 +619,7 @@ class IngredientSimpleSerializer(WritableNestedModelSerializer):
|
||||
used_in_recipes = serializers.SerializerMethodField('get_used_in_recipes')
|
||||
amount = CustomDecimalField()
|
||||
conversions = serializers.SerializerMethodField('get_conversions')
|
||||
nutritions = serializers.SerializerMethodField('get_nutritions')
|
||||
|
||||
def get_used_in_recipes(self, obj):
|
||||
return list(Recipe.objects.filter(steps__ingredients=obj.id).values('id', 'name'))
|
||||
@ -629,7 +630,7 @@ class IngredientSimpleSerializer(WritableNestedModelSerializer):
|
||||
for c in UnitConversion.objects.filter(space=self.context['request'].space).filter(Q(food__isnull=True) | Q(food=obj.food)).filter(Q(base_unit=obj.unit) | Q(converted_unit=obj.unit)):
|
||||
if obj.unit == c.base_unit:
|
||||
conversions.append({
|
||||
'amount': obj.amount * (c.converted_amount/c.base_amount),
|
||||
'amount': obj.amount * (c.converted_amount / c.base_amount),
|
||||
'unit': UnitSerializer(c.converted_unit, context={'request': self.context['request']}).data,
|
||||
})
|
||||
else:
|
||||
@ -640,6 +641,23 @@ class IngredientSimpleSerializer(WritableNestedModelSerializer):
|
||||
|
||||
return conversions
|
||||
|
||||
def get_nutritions(self, ingredient):
|
||||
nutritions = {}
|
||||
for nt in NutritionType.objects.filter(space=self.context['request'].space).all():
|
||||
nutritions[nt.id] = None
|
||||
|
||||
food_nutrition = ingredient.food.foodnutrition_set.all()
|
||||
for fn in food_nutrition:
|
||||
if fn.food_unit == ingredient.unit:
|
||||
nutritions[fn.nutrition_type.id] = ingredient.amount / fn.food_amount * fn.nutrition_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
|
||||
|
||||
return nutritions
|
||||
|
||||
def create(self, validated_data):
|
||||
validated_data['space'] = self.context['request'].space
|
||||
return super().create(validated_data)
|
||||
@ -651,7 +669,7 @@ class IngredientSimpleSerializer(WritableNestedModelSerializer):
|
||||
class Meta:
|
||||
model = Ingredient
|
||||
fields = (
|
||||
'id', 'food', 'unit', 'amount', 'conversions', 'note', 'order',
|
||||
'id', 'food', 'unit', 'amount', 'nutritions', 'conversions', 'note', 'order',
|
||||
'is_header', 'no_amount', 'original_text', 'used_in_recipes',
|
||||
'always_use_plural_unit', 'always_use_plural_food',
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user