added recipe properties

This commit is contained in:
vabene1111 2023-05-06 19:14:25 +02:00
parent 763f71a05c
commit 60f31608b9
17 changed files with 1099 additions and 735 deletions

View File

@ -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, FoodPropertyType, FoodProperty)
TelegramBot, Unit, UserFile, UserPreference, ViewLog, Automation, UserSpace, UnitConversion, PropertyType, FoodProperty)
class CustomUserAdmin(UserAdmin):
@ -331,7 +331,7 @@ class FoodPropertyTypeAdmin(admin.ModelAdmin):
list_display = ('id', 'name')
admin.site.register(FoodPropertyType, FoodPropertyTypeAdmin)
admin.site.register(PropertyType, FoodPropertyTypeAdmin)
class FoodPropertyAdmin(admin.ModelAdmin):

View File

@ -1,6 +1,6 @@
from django.db.models import Q
from cookbook.models import Unit, SupermarketCategory, FoodProperty, FoodPropertyType, Supermarket, SupermarketCategoryRelation, Food, Automation, UnitConversion
from cookbook.models import Unit, SupermarketCategory, FoodProperty, PropertyType, Supermarket, SupermarketCategoryRelation, Food, Automation, UnitConversion
class OpenDataImporter:
@ -55,14 +55,14 @@ class OpenDataImporter:
insert_list = []
for k in list(self.data[datatype].keys()):
insert_list.append(FoodPropertyType(
insert_list.append(PropertyType(
name=self.data[datatype][k]['name'],
unit=self.data[datatype][k]['unit'],
open_data_slug=k,
space=self.request.space
))
return FoodPropertyType.objects.bulk_create(insert_list, update_conflicts=True, update_fields=('open_data_slug',), unique_fields=('space', 'name',))
return PropertyType.objects.bulk_create(insert_list, update_conflicts=True, update_fields=('open_data_slug',), unique_fields=('space', 'name',))
def import_supermarket(self):
datatype = 'supermarket'
@ -114,7 +114,7 @@ class OpenDataImporter:
existing_objects[f[2]] = f
self._update_slug_cache(Unit, 'unit')
self._update_slug_cache(FoodPropertyType, 'property')
self._update_slug_cache(PropertyType, 'property')
pref_unit_key = 'preferred_unit_metric'
pref_shopping_unit_key = 'preferred_packaging_unit_metric'

View File

@ -1,4 +1,4 @@
from cookbook.models import FoodPropertyType, Unit, Food, FoodProperty, Recipe, Step
from cookbook.models import PropertyType, Unit, Food, FoodProperty, Recipe, Step
class FoodPropertyHelper:
@ -19,7 +19,7 @@ class FoodPropertyHelper:
"""
ingredients = []
computed_properties = {}
property_types = FoodPropertyType.objects.filter(space=self.space).all()
property_types = PropertyType.objects.filter(space=self.space).all()
for s in recipe.steps.all():
ingredients += s.ingredients.all()
@ -64,10 +64,10 @@ class FoodPropertyHelper:
food_1 = Food.objects.create(name='Food 1', space=self.space)
food_2 = Food.objects.create(name='Food 2', space=self.space)
property_fat = FoodPropertyType.objects.create(name='Fat', unit='g', space=self.space)
property_calories = FoodPropertyType.objects.create(name='Calories', unit='kcal', space=self.space)
property_nuts = FoodPropertyType.objects.create(name='Nuts', space=self.space)
property_price = FoodPropertyType.objects.create(name='Price', unit='', space=self.space)
property_fat = PropertyType.objects.create(name='Fat', unit='g', space=self.space)
property_calories = PropertyType.objects.create(name='Calories', unit='kcal', space=self.space)
property_nuts = PropertyType.objects.create(name='Nuts', space=self.space)
property_price = PropertyType.objects.create(name='Price', unit='', space=self.space)
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=self.space)
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=self.space)
@ -87,3 +87,18 @@ class FoodPropertyHelper:
step_2 = Step.objects.create(instruction='instruction_step_1', space=self.space)
step_2.ingredients.create(amount=50, unit=unit_gram, food=food_1, space=self.space)
recipe_1.steps.add(step_2)
class RecipePropertyHelper:
space = None
def __init__(self, space):
"""
Helper to perform recipe property operations
:param space: space to limit scope to
"""
self.space = space
def parse_properties_from_schema(self, schema):
pass

View File

@ -1,5 +1,6 @@
# import random
import re
import traceback
from html import unescape
from django.core.cache import caches
@ -12,7 +13,8 @@ from recipe_scrapers._utils import get_host_name, get_minutes
# from cookbook.helper import recipe_url_import as helper
from cookbook.helper.ingredient_parser import IngredientParser
from cookbook.models import Automation, Keyword
from cookbook.models import Automation, Keyword, PropertyType
# from unicodedata import decomposition
@ -193,6 +195,13 @@ def get_from_scraper(scrape, request):
except Exception:
pass
try:
recipe_json['properties'] = get_recipe_properties(request.space, scrape.schema.nutrients())
print(recipe_json['properties'])
except Exception:
traceback.print_exc()
pass
if recipe_json['source_url']:
automations = Automation.objects.filter(type=Automation.INSTRUCTION_REPLACE, space=request.space, disabled=False).only('param_1', 'param_2', 'param_3').order_by('order').all()[:512]
for a in automations:
@ -203,6 +212,30 @@ def get_from_scraper(scrape, request):
return recipe_json
def get_recipe_properties(space, property_data):
# {'servingSize': '1', 'calories': '302 kcal', 'proteinContent': '7,66g', 'fatContent': '11,56g', 'carbohydrateContent': '41,33g'}
properties = {
"property-calories": "calories",
"property-carbohydrates": "carbohydrateContent",
"property-proteins": "proteinContent",
"property-fats": "fatContent",
}
recipe_properties = []
for pt in PropertyType.objects.filter(space=space, open_data_slug__in=list(properties.keys())).all():
for p in list(properties.keys()):
if pt.open_data_slug == p:
if properties[p] in property_data:
recipe_properties.append({
'property_type': {
'id': pt.id,
'name': pt.name,
},
'property_amount': parse_servings(property_data[properties[p]]) / float(property_data['servingSize']),
})
return recipe_properties
def get_from_youtube_scraper(url, request):
"""A YouTube Information Scraper."""
kw, created = Keyword.objects.get_or_create(name='YouTube', space=request.space)

View File

@ -0,0 +1,34 @@
# Generated by Django 4.1.7 on 2023-05-06 16:33
import cookbook.models
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('cookbook', '0194_supermarketcategoryrelation_unique_sm_category_relation'),
]
operations = [
migrations.RenameModel(
old_name='FoodPropertyType',
new_name='PropertyType',
),
migrations.CreateModel(
name='RecipeProperty',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('property_amount', models.DecimalField(decimal_places=4, default=0, max_digits=32)),
('property_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='cookbook.propertytype')),
('space', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.space')),
],
bases=(models.Model, cookbook.models.PermissionModelMixin),
),
migrations.AddField(
model_name='recipe',
name='properties',
field=models.ManyToManyField(blank=True, to='cookbook.recipeproperty'),
),
]

View File

@ -755,7 +755,7 @@ class Step(ExportModelOperationsMixin('step'), models.Model, PermissionModelMixi
indexes = (GinIndex(fields=["search_vector"]),)
class FoodPropertyType(models.Model, PermissionModelMixin):
class PropertyType(models.Model, PermissionModelMixin):
NUTRITION = 'NUTRITION'
ALLERGEN = 'ALLERGEN'
PRICE = 'PRICE'
@ -780,7 +780,7 @@ class FoodPropertyType(models.Model, PermissionModelMixin):
class Meta:
constraints = [
models.UniqueConstraint(fields=['space', 'name'], name='food_property_type_unique_name_per_space')
models.UniqueConstraint(fields=['space', 'name'], name='property_type_unique_name_per_space')
]
@ -789,7 +789,7 @@ class FoodProperty(models.Model, PermissionModelMixin):
food_unit = models.ForeignKey(Unit, on_delete=models.CASCADE)
food = models.ForeignKey(Food, on_delete=models.CASCADE)
property_amount = models.DecimalField(default=0, decimal_places=4, max_digits=32)
property_type = models.ForeignKey(FoodPropertyType, on_delete=models.PROTECT)
property_type = models.ForeignKey(PropertyType, on_delete=models.PROTECT)
space = models.ForeignKey(Space, on_delete=models.CASCADE)
objects = ScopedManager(space='space')
@ -803,6 +803,17 @@ class FoodProperty(models.Model, PermissionModelMixin):
]
class RecipeProperty(models.Model, PermissionModelMixin):
property_amount = models.DecimalField(default=0, decimal_places=4, max_digits=32)
property_type = models.ForeignKey(PropertyType, on_delete=models.PROTECT)
space = models.ForeignKey(Space, on_delete=models.CASCADE)
objects = ScopedManager(space='space')
def __str__(self):
return f'{self.property_amount} {self.property_type.unit} {self.property_type.name}'
class NutritionInformation(models.Model, PermissionModelMixin):
fats = models.DecimalField(default=0, decimal_places=16, max_digits=32)
carbohydrates = models.DecimalField(
@ -841,6 +852,7 @@ class Recipe(ExportModelOperationsMixin('recipe'), models.Model, PermissionModel
waiting_time = models.IntegerField(default=0)
internal = models.BooleanField(default=False)
nutrition = models.ForeignKey(NutritionInformation, blank=True, null=True, on_delete=models.CASCADE)
properties = models.ManyToManyField(RecipeProperty, blank=True)
show_ingredient_overview = models.BooleanField(default=True)
private = models.BooleanField(default=False)
shared = models.ManyToManyField(User, blank=True, related_name='recipe_shared_with')

View File

@ -22,7 +22,7 @@ from rest_framework.exceptions import NotFound, ValidationError
from cookbook.helper.CustomStorageClass import CachedS3Boto3Storage
from cookbook.helper.HelperFunctions import str2bool
from cookbook.helper.food_property_helper import FoodPropertyHelper
from cookbook.helper.property_helper import FoodPropertyHelper
from cookbook.helper.permission_helper import above_space_limit
from cookbook.helper.shopping_helper import RecipeShoppingEditor
from cookbook.helper.unit_conversion_helper import UnitConversionHelper
@ -33,7 +33,7 @@ from cookbook.models import (Automation, BookmarkletImport, Comment, CookLog, Cu
ShoppingListEntry, ShoppingListRecipe, Space, Step, Storage,
Supermarket, SupermarketCategory, SupermarketCategoryRelation, Sync,
SyncLog, Unit, UserFile, UserPreference, UserSpace, ViewLog, UnitConversion, FoodProperty,
FoodPropertyType)
PropertyType, RecipeProperty)
from cookbook.templatetags.custom_tags import markdown
from recipes.settings import AWS_ENABLED, MEDIA_URL
@ -747,18 +747,18 @@ class UnitConversionSerializer(WritableNestedModelSerializer):
fields = ('id', 'name', 'base_amount', 'base_unit', 'converted_amount', 'converted_unit', 'food', 'open_data_slug')
class FoodPropertyTypeSerializer(serializers.ModelSerializer):
class PropertyTypeSerializer(serializers.ModelSerializer):
def create(self, validated_data):
validated_data['space'] = self.context['request'].space
return super().create(validated_data)
class Meta:
model = FoodPropertyType
model = PropertyType
fields = ('id', 'name', 'icon', 'unit', 'description', 'open_data_slug')
class FoodPropertySerializer(UniqueFieldsMixin, WritableNestedModelSerializer):
property_type = FoodPropertyTypeSerializer()
property_type = PropertyTypeSerializer()
food = FoodSimpleSerializer()
food_unit = UnitSerializer()
food_amount = CustomDecimalField()
@ -776,6 +776,20 @@ class FoodPropertySerializer(UniqueFieldsMixin, WritableNestedModelSerializer):
read_only_fields = ('id',)
class RecipePropertySerializer(UniqueFieldsMixin, WritableNestedModelSerializer):
property_type = PropertyTypeSerializer()
property_amount = CustomDecimalField()
def create(self, validated_data):
validated_data['space'] = self.context['request'].space
return super().create(validated_data)
class Meta:
model = RecipeProperty
fields = ('id', 'property_type', 'property_amount',)
read_only_fields = ('id',)
class NutritionInformationSerializer(serializers.ModelSerializer):
carbohydrates = CustomDecimalField()
fats = CustomDecimalField()
@ -826,6 +840,7 @@ class RecipeOverviewSerializer(RecipeBaseSerializer):
class RecipeSerializer(RecipeBaseSerializer):
nutrition = NutritionInformationSerializer(allow_null=True, required=False)
properties = RecipePropertySerializer(many=True, required=False)
steps = StepSerializer(many=True)
keywords = KeywordSerializer(many=True)
shared = UserSerializer(many=True, required=False)
@ -842,7 +857,7 @@ class RecipeSerializer(RecipeBaseSerializer):
fields = (
'id', 'name', 'description', 'image', 'keywords', 'steps', 'working_time',
'waiting_time', 'created_by', 'created_at', 'updated_at', 'source_url',
'internal', 'show_ingredient_overview', 'nutrition', 'food_properties', 'servings', 'file_path', 'servings_text', 'rating',
'internal', 'show_ingredient_overview', 'nutrition', 'properties', 'food_properties', 'servings', 'file_path', 'servings_text', 'rating',
'last_cooked',
'private', 'shared',
)

View File

@ -270,7 +270,7 @@
</a>
</div>
<div class="col-4">
<a href="{% url 'list_food_property_type' %}" class="p-0 p-md-1">
<a href="{% url 'list_property_type' %}" class="p-0 p-md-1">
<div class="card p-0 no-gutters border-0">
<div class="card-body text-center p-0 no-gutters">
<i class="fas fa-database fa-2x"></i>

View File

@ -1,8 +1,8 @@
from django.contrib import auth
from django_scopes import scopes_disabled
from cookbook.helper.food_property_helper import FoodPropertyHelper
from cookbook.models import Unit, Food, FoodPropertyType, FoodProperty, Recipe, Step
from cookbook.helper.property_helper import FoodPropertyHelper
from cookbook.models import Unit, Food, PropertyType, FoodProperty, Recipe, Step
def test_food_property(space_1, u1_s1):
@ -16,10 +16,10 @@ def test_food_property(space_1, u1_s1):
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)
property_fat = PropertyType.objects.create(name='property_fat', space=space_1)
property_calories = PropertyType.objects.create(name='property_calories', space=space_1)
property_nuts = PropertyType.objects.create(name='property_nuts', space=space_1)
property_price = PropertyType.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)

View File

@ -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, FoodPropertyType, UnitConversion)
get_model_name, UserSpace, Space, PropertyType, UnitConversion)
from .views import api, data, delete, edit, import_export, lists, new, telegram, views
from .views.api import CustomAuthToken, ImportOpenData
@ -193,7 +193,7 @@ for m in generic_models:
)
)
vue_models = [Food, Keyword, Unit, Supermarket, SupermarketCategory, Automation, UserFile, Step, CustomFilter, UnitConversion, FoodPropertyType]
vue_models = [Food, Keyword, Unit, Supermarket, SupermarketCategory, Automation, UserFile, Step, CustomFilter, UnitConversion, PropertyType]
for m in vue_models:
py_name = get_model_name(m)
url_name = py_name.replace('_', '-')

View File

@ -70,7 +70,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, FoodPropertyType, FoodProperty)
SyncLog, Unit, UserFile, UserPreference, UserSpace, ViewLog, UnitConversion, PropertyType, FoodProperty)
from cookbook.provider.dropbox import Dropbox
from cookbook.provider.local import Local
from cookbook.provider.nextcloud import Nextcloud
@ -94,7 +94,7 @@ from cookbook.serializer import (AutomationSerializer, BookmarkletImportListSeri
SyncLogSerializer, SyncSerializer, UnitSerializer,
UserFileSerializer, UserSerializer, UserPreferenceSerializer,
UserSpaceSerializer, ViewLogSerializer, AccessTokenSerializer, FoodSimpleSerializer,
RecipeExportSerializer, UnitConversionSerializer, FoodPropertyTypeSerializer, FoodPropertySerializer)
RecipeExportSerializer, UnitConversionSerializer, PropertyTypeSerializer, FoodPropertySerializer)
from cookbook.views.import_export import get_integration
from recipes import settings
@ -811,8 +811,12 @@ class RecipeViewSet(viewsets.ModelViewSet):
if self.detail: # if detail request and not list, private condition is verified by permission class
if not share: # filter for space only if not shared
self.queryset = self.queryset.filter(space=self.request.space).prefetch_related('steps', 'keywords',
self.queryset = self.queryset.filter(space=self.request.space).prefetch_related(
'keywords',
'shared',
'properties',
'properties__property_type',
'steps',
'steps__ingredients',
'steps__ingredients__step_set',
'steps__ingredients__step_set__recipe_set',
@ -831,9 +835,8 @@ class RecipeViewSet(viewsets.ModelViewSet):
'steps__ingredients__unit__unit_conversion_base_relation__base_unit',
'steps__ingredients__unit__unit_conversion_converted_relation',
'steps__ingredients__unit__unit_conversion_converted_relation__converted_unit',
'cooklog_set').select_related('nutrition')
'cooklog_set').select_related(
'nutrition')
return super().get_queryset()
self.queryset = self.queryset.filter(space=self.request.space).filter(
@ -972,8 +975,8 @@ class UnitConversionViewSet(viewsets.ModelViewSet):
class FoodPropertyTypeViewSet(viewsets.ModelViewSet):
queryset = FoodPropertyType.objects
serializer_class = FoodPropertyTypeSerializer
queryset = PropertyType.objects
serializer_class = PropertyTypeSerializer
permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
def get_queryset(self):

View File

@ -246,15 +246,15 @@ def unit_conversion(request):
@group_required('user')
def food_property_type(request):
def property_type(request):
# model-name is the models.js name of the model, probably ALL-CAPS
return render(
request,
'generic/model_template.html',
{
"title": _("Food Property Types"),
"title": _("Property Types"),
"config": {
'model': "FOOD_PROPERTY_TYPE", # *REQUIRED* name of the model in models.js
'model': "PROPERTY_TYPE", # *REQUIRED* name of the model in models.js
}
}
)

View File

@ -23,7 +23,7 @@ from oauth2_provider.models import AccessToken
from cookbook.forms import (CommentForm, Recipe, SearchPreferenceForm, ShoppingPreferenceForm,
SpaceCreateForm, SpaceJoinForm, User,
UserCreateForm, UserNameForm, UserPreference, UserPreferenceForm)
from cookbook.helper.food_property_helper import FoodPropertyHelper
from cookbook.helper.property_helper import FoodPropertyHelper
from cookbook.helper.permission_helper import group_required, has_group_permission, share_link_valid, switch_user_active_space
from cookbook.models import (Comment, CookLog, InviteLink, SearchFields, SearchPreference, ShareLink,
Space, ViewLog, UserSpace)

View File

@ -31,7 +31,7 @@
</div>
</div>
<!-- Image and misc properties -->
<!-- Image and misc -->
<div class="row pt-2">
<div class="col-md-6" style="max-height: 50vh; min-height: 30vh">
<input id="id_file_upload" ref="file_upload" type="file" hidden
@ -99,65 +99,53 @@
</div>
</div>
<!-- Nutrition -->
<div class="row pt-2">
<div class="col-md-12">
<div class="card border-grey">
<div class="card-header" style="display: table">
<div class="row">
<div class="col-md-9 d-table">
<h5 class="d-table-cell align-middle">{{ $t("Nutrition") }}</h5>
<div class="card mt-2 mb-2">
<div class="card-body pr-2 pl-2 pr-md-5 pl-md-5 pt-3 pb-3">
<h6>{{ $t('Properties') }} <small class="text-muted"> {{$t('per_serving')}}</small></h6>
<div class="alert alert-info" role="alert">
{{ $t('recipe_property_info')}}
</div>
<div class="d-flex mt-2" v-for="p in recipe.properties" v-bind:key="p.id">
<div class="flex-fill w-50">
<generic-multiselect
@change="p.property_type = $event.val"
:initial_single_selection="p.property_type"
:label="'name'"
:model="Models.PROPERTY_TYPE"
:limit="25"
:multiple="false"
></generic-multiselect>
</div>
<div class="flex-fill w-50">
<div class="input-group">
<input type="number" class="form-control" v-model="p.property_amount">
<div class="input-group-append">
<span class="input-group-text" v-if="p.property_type !== null && p.property_type.unit !== ''">{{ p.property_type.unit }}</span>
<button class="btn btn-danger" @click="deleteProperty(p)"><i class="fa fa-trash fa-fw"></i></button>
</div>
</div>
</div>
</div>
<div class="flex-row mt-2">
<div class="flex-column w-25 offset-4">
<button class="btn btn-success btn-block" @click="addProperty()"><i class="fa fa-plus"></i></button>
</div>
</div>
</div>
<div class="col-md-3">
<button
type="button"
@click="addNutrition()"
v-if="recipe.nutrition === null"
v-b-tooltip.hover
v-bind:title="$t('Add_nutrition_recipe')"
class="btn btn-sm btn-success shadow-none float-right"
>
<i class="fas fa-plus-circle"></i>
</button>
<button
type="button"
@click="removeNutrition()"
v-if="recipe.nutrition !== null"
v-b-tooltip.hover
v-bind:title="$t('Remove_nutrition_recipe')"
class="btn btn-sm btn-danger shadow-none float-right"
>
<i class="fas fa-trash-alt"></i>
</button>
</div>
</div>
</div>
<b-collapse id="id_nutrition_collapse" class="mt-2" v-model="nutrition_visible">
<div class="card-body" v-if="recipe.nutrition !== null">
<b-alert show>
There is currently only very basic support for tracking nutritional information. A
<a href="https://github.com/vabene1111/recipes/issues/896" target="_blank"
rel="noreferrer nofollow">big update</a> is planned to improve on this in many
different areas.
</b-alert>
<div class="row pt-2">
<div class="col-md-12">
<label for="id_name"> {{ $t(energy()) }}</label>
<input class="form-control" id="id_calories" v-model="recipe.nutrition.calories"/>
<label for="id_name"> {{ $t("Carbohydrates") }}</label>
<input class="form-control" id="id_carbohydrates"
v-model="recipe.nutrition.carbohydrates"/>
<label for="id_name"> {{ $t("Fats") }}</label>
<input class="form-control" id="id_fats" v-model="recipe.nutrition.fats"/>
<label for="id_name"> {{ $t("Proteins") }}</label>
<input class="form-control" id="id_proteins" v-model="recipe.nutrition.proteins"/>
</div>
</b-collapse>
</div>
<b-card-header header-tag="header" class="p-1" role="tab">
<b-button squared block v-b-toggle.additional_collapse class="text-left"
variant="outline-primary">{{ $t("additional_options") }}
@ -1121,6 +1109,14 @@ export default {
let new_keyword = {label: tag, name: tag}
this.recipe.keywords.push(new_keyword)
},
addProperty: function () {
this.recipe.properties.push(
{'property_amount': 0, 'property_type': null}
)
},
deleteProperty: function (recipe_property) {
this.recipe.properties = this.recipe.properties.filter(p => p.id !== recipe_property.id)
},
searchKeywords: function (query) {
let apiFactory = new ApiApiFactory()

View File

@ -14,6 +14,7 @@
"success_moving_resource": "Successfully moved a resource!",
"success_merging_resource": "Successfully merged a resource!",
"file_upload_disabled": "File upload is not enabled for your space.",
"recipe_property_info": "You can also add properties to foods to calculate them automatically based on your recipe!",
"warning_space_delete": "You can delete your space including all recipes, shopping lists, meal plans and whatever else you have created. This cannot be undone! Are you sure you want to do this ?",
"food_inherit_info": "Fields on food that should be inherited by default.",
"facet_count_info": "Show recipe counts on search filters.",
@ -37,6 +38,7 @@
"Add_nutrition_recipe": "Add nutrition to recipe",
"Remove_nutrition_recipe": "Delete nutrition from recipe",
"Copy_template_reference": "Copy template reference",
"per_serving": "per servings",
"Save_and_View": "Save & View",
"Manage_Books": "Manage Books",
"Meal_Plan": "Meal Plan",

View File

@ -659,9 +659,9 @@ export class Models {
},
}
static FOOD_PROPERTY_TYPE = {
name: "Food Property Type",
apiName: "FoodPropertyType",
static PROPERTY_TYPE = {
name: "Property Type",
apiName: "PropertyType",
paginated: false,
list: {
header_component: {

File diff suppressed because it is too large Load Diff