basics
This commit is contained in:
parent
56252a707a
commit
6176eeb024
@ -28,7 +28,7 @@ class IngredientParser:
|
|||||||
self.food_aliases = c
|
self.food_aliases = c
|
||||||
caches['default'].touch(FOOD_CACHE_KEY, 30)
|
caches['default'].touch(FOOD_CACHE_KEY, 30)
|
||||||
else:
|
else:
|
||||||
for a in Automation.objects.filter(space=self.request.space, disabled=False, type=Automation.FOOD_ALIAS).only('param_1', 'param_2').all():
|
for a in Automation.objects.filter(space=self.request.space, disabled=False, type=Automation.FOOD_ALIAS).only('param_1', 'param_2').order_by('order').all():
|
||||||
self.food_aliases[a.param_1] = a.param_2
|
self.food_aliases[a.param_1] = a.param_2
|
||||||
caches['default'].set(FOOD_CACHE_KEY, self.food_aliases, 30)
|
caches['default'].set(FOOD_CACHE_KEY, self.food_aliases, 30)
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ class IngredientParser:
|
|||||||
self.unit_aliases = c
|
self.unit_aliases = c
|
||||||
caches['default'].touch(UNIT_CACHE_KEY, 30)
|
caches['default'].touch(UNIT_CACHE_KEY, 30)
|
||||||
else:
|
else:
|
||||||
for a in Automation.objects.filter(space=self.request.space, disabled=False, type=Automation.UNIT_ALIAS).only('param_1', 'param_2').all():
|
for a in Automation.objects.filter(space=self.request.space, disabled=False, type=Automation.UNIT_ALIAS).only('param_1', 'param_2').order_by('order').all():
|
||||||
self.unit_aliases[a.param_1] = a.param_2
|
self.unit_aliases[a.param_1] = a.param_2
|
||||||
caches['default'].set(UNIT_CACHE_KEY, self.unit_aliases, 30)
|
caches['default'].set(UNIT_CACHE_KEY, self.unit_aliases, 30)
|
||||||
else:
|
else:
|
||||||
@ -59,7 +59,7 @@ class IngredientParser:
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
return food
|
return food
|
||||||
else:
|
else:
|
||||||
if automation := Automation.objects.filter(space=self.request.space, type=Automation.FOOD_ALIAS, param_1=food, disabled=False).first():
|
if automation := Automation.objects.filter(space=self.request.space, type=Automation.FOOD_ALIAS, param_1=food, disabled=False).order_by('order').first():
|
||||||
return automation.param_2
|
return automation.param_2
|
||||||
return food
|
return food
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ class IngredientParser:
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
return unit
|
return unit
|
||||||
else:
|
else:
|
||||||
if automation := Automation.objects.filter(space=self.request.space, type=Automation.UNIT_ALIAS, param_1=unit, disabled=False).first():
|
if automation := Automation.objects.filter(space=self.request.space, type=Automation.UNIT_ALIAS, param_1=unit, disabled=False).order_by('order').first():
|
||||||
return automation.param_2
|
return automation.param_2
|
||||||
return unit
|
return unit
|
||||||
|
|
||||||
|
@ -12,7 +12,8 @@ from recipe_scrapers._utils import get_host_name, get_minutes
|
|||||||
|
|
||||||
from cookbook.helper import recipe_url_import as helper
|
from cookbook.helper import recipe_url_import as helper
|
||||||
from cookbook.helper.ingredient_parser import IngredientParser
|
from cookbook.helper.ingredient_parser import IngredientParser
|
||||||
from cookbook.models import Keyword
|
from cookbook.models import Keyword, Automation
|
||||||
|
|
||||||
|
|
||||||
# from recipe_scrapers._utils import get_minutes ## temporary until/unless upstream incorporates get_minutes() PR
|
# from recipe_scrapers._utils import get_minutes ## temporary until/unless upstream incorporates get_minutes() PR
|
||||||
|
|
||||||
@ -121,7 +122,7 @@ def get_from_scraper(scrape, request):
|
|||||||
try:
|
try:
|
||||||
keywords.append(source_url.replace('http://', '').replace('https://', '').split('/')[0])
|
keywords.append(source_url.replace('http://', '').replace('https://', '').split('/')[0])
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
recipe_json['source_url'] = ''
|
||||||
|
|
||||||
try:
|
try:
|
||||||
recipe_json['keywords'] = parse_keywords(list(set(map(str.casefold, keywords))), request.space)
|
recipe_json['keywords'] = parse_keywords(list(set(map(str.casefold, keywords))), request.space)
|
||||||
@ -139,10 +140,18 @@ def get_from_scraper(scrape, request):
|
|||||||
if len(recipe_json['steps']) == 0:
|
if len(recipe_json['steps']) == 0:
|
||||||
recipe_json['steps'].append({'instruction': '', 'ingredients': [], })
|
recipe_json['steps'].append({'instruction': '', 'ingredients': [], })
|
||||||
|
|
||||||
if len(parse_description(description)) > 256: # split at 256 as long descriptions dont look good on recipe cards
|
parsed_description = parse_description(description)
|
||||||
recipe_json['steps'][0]['instruction'] = f'*{parse_description(description)}* \n\n' + recipe_json['steps'][0]['instruction']
|
# TODO notify user about limit if reached
|
||||||
|
# limits exist to limit the attack surface for dos style attacks
|
||||||
|
automations = Automation.objects.filter(type=Automation.DESCRIPTION_REPLACE, space=request.space, disabled=False).only('param_1', 'param_2', 'param_3').all().order_by('order')[:512]
|
||||||
|
for a in automations:
|
||||||
|
if re.match(a.param_1, (recipe_json['source_url'])[:512]):
|
||||||
|
parsed_description = re.sub(a.param_2, a.param_3, parsed_description, count=1)
|
||||||
|
|
||||||
|
if len(parsed_description) > 256: # split at 256 as long descriptions don't look good on recipe cards
|
||||||
|
recipe_json['steps'][0]['instruction'] = f'*{parsed_description}* \n\n' + recipe_json['steps'][0]['instruction']
|
||||||
else:
|
else:
|
||||||
recipe_json['description'] = parse_description(description)[:512]
|
recipe_json['description'] = parsed_description[:512]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
for x in scrape.ingredients():
|
for x in scrape.ingredients():
|
||||||
@ -175,6 +184,12 @@ def get_from_scraper(scrape, request):
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
if recipe_json['source_url']:
|
||||||
|
automations = Automation.objects.filter(type=Automation.DESCRIPTION_REPLACE, space=request.space, disabled=False).only('param_1', 'param_2', 'param_3').order_by('order').all()[:512]
|
||||||
|
for a in automations:
|
||||||
|
if re.match(a.param_1, (recipe_json['source_url'])[:512]):
|
||||||
|
recipe_json['description'] = re.sub(a.param_2, a.param_3, recipe_json['description'], count=1)
|
||||||
|
|
||||||
return recipe_json
|
return recipe_json
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 4.1.4 on 2023-01-03 21:38
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('cookbook', '0185_food_plural_name_ingredient_always_use_plural_food_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='automation',
|
||||||
|
name='order',
|
||||||
|
field=models.IntegerField(default=1000),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='automation',
|
||||||
|
name='type',
|
||||||
|
field=models.CharField(choices=[('FOOD_ALIAS', 'Food Alias'), ('UNIT_ALIAS', 'Unit Alias'), ('KEYWORD_ALIAS', 'Keyword Alias'), ('DESCRIPTION_REPLACE', 'Description Replace'), ('INSTRUCTION_REPLACE', 'Instruction Replace')], max_length=128),
|
||||||
|
),
|
||||||
|
]
|
@ -367,7 +367,7 @@ class UserPreference(models.Model, PermissionModelMixin):
|
|||||||
)
|
)
|
||||||
|
|
||||||
user = AutoOneToOneField(User, on_delete=models.CASCADE, primary_key=True)
|
user = AutoOneToOneField(User, on_delete=models.CASCADE, primary_key=True)
|
||||||
image = models.ForeignKey("UserFile", on_delete=models.SET_NULL, null=True,blank=True, related_name='user_image')
|
image = models.ForeignKey("UserFile", on_delete=models.SET_NULL, null=True, blank=True, related_name='user_image')
|
||||||
theme = models.CharField(choices=THEMES, max_length=128, default=TANDOOR)
|
theme = models.CharField(choices=THEMES, max_length=128, default=TANDOOR)
|
||||||
nav_color = models.CharField(choices=COLORS, max_length=128, default=PRIMARY)
|
nav_color = models.CharField(choices=COLORS, max_length=128, default=PRIMARY)
|
||||||
default_unit = models.CharField(max_length=32, default='g')
|
default_unit = models.CharField(max_length=32, default='g')
|
||||||
@ -1223,9 +1223,12 @@ class Automation(ExportModelOperationsMixin('automations'), models.Model, Permis
|
|||||||
FOOD_ALIAS = 'FOOD_ALIAS'
|
FOOD_ALIAS = 'FOOD_ALIAS'
|
||||||
UNIT_ALIAS = 'UNIT_ALIAS'
|
UNIT_ALIAS = 'UNIT_ALIAS'
|
||||||
KEYWORD_ALIAS = 'KEYWORD_ALIAS'
|
KEYWORD_ALIAS = 'KEYWORD_ALIAS'
|
||||||
|
DESCRIPTION_REPLACE = 'DESCRIPTION_REPLACE'
|
||||||
|
INSTRUCTION_REPLACE = 'INSTRUCTION_REPLACE'
|
||||||
|
|
||||||
type = models.CharField(max_length=128,
|
type = models.CharField(max_length=128,
|
||||||
choices=((FOOD_ALIAS, _('Food Alias')), (UNIT_ALIAS, _('Unit Alias')), (KEYWORD_ALIAS, _('Keyword Alias')),))
|
choices=((FOOD_ALIAS, _('Food Alias')), (UNIT_ALIAS, _('Unit Alias')), (KEYWORD_ALIAS, _('Keyword Alias')),
|
||||||
|
(DESCRIPTION_REPLACE, _('Description Replace')), (INSTRUCTION_REPLACE, _('Instruction Replace')),))
|
||||||
name = models.CharField(max_length=128, default='')
|
name = models.CharField(max_length=128, default='')
|
||||||
description = models.TextField(blank=True, null=True)
|
description = models.TextField(blank=True, null=True)
|
||||||
|
|
||||||
@ -1233,6 +1236,8 @@ class Automation(ExportModelOperationsMixin('automations'), models.Model, Permis
|
|||||||
param_2 = models.CharField(max_length=128, blank=True, null=True)
|
param_2 = models.CharField(max_length=128, blank=True, null=True)
|
||||||
param_3 = models.CharField(max_length=128, blank=True, null=True)
|
param_3 = models.CharField(max_length=128, blank=True, null=True)
|
||||||
|
|
||||||
|
order = models.IntegerField(default=1000)
|
||||||
|
|
||||||
disabled = models.BooleanField(default=False)
|
disabled = models.BooleanField(default=False)
|
||||||
|
|
||||||
updated_at = models.DateTimeField(auto_now=True)
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
@ -876,11 +876,11 @@ class ShoppingListRecipeSerializer(serializers.ModelSerializer):
|
|||||||
value = value.quantize(
|
value = value.quantize(
|
||||||
Decimal(1)) if value == value.to_integral() else value.normalize() # strips trailing zero
|
Decimal(1)) if value == value.to_integral() else value.normalize() # strips trailing zero
|
||||||
return (
|
return (
|
||||||
obj.name
|
obj.name
|
||||||
or getattr(obj.mealplan, 'title', None)
|
or getattr(obj.mealplan, 'title', None)
|
||||||
or (d := getattr(obj.mealplan, 'date', None)) and ': '.join([obj.mealplan.recipe.name, str(d)])
|
or (d := getattr(obj.mealplan, 'date', None)) and ': '.join([obj.mealplan.recipe.name, str(d)])
|
||||||
or obj.recipe.name
|
or obj.recipe.name
|
||||||
) + f' ({value:.2g})'
|
) + f' ({value:.2g})'
|
||||||
|
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
# TODO remove once old shopping list
|
# TODO remove once old shopping list
|
||||||
@ -1067,7 +1067,7 @@ class AutomationSerializer(serializers.ModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Automation
|
model = Automation
|
||||||
fields = (
|
fields = (
|
||||||
'id', 'type', 'name', 'description', 'param_1', 'param_2', 'param_3', 'disabled', 'created_by',)
|
'id', 'type', 'name', 'description', 'param_1', 'param_2', 'param_3', 'order', 'disabled', 'created_by',)
|
||||||
read_only_fields = ('created_by',)
|
read_only_fields = ('created_by',)
|
||||||
|
|
||||||
|
|
||||||
|
@ -72,3 +72,13 @@ def test_recipe_import(arg, u1_s1):
|
|||||||
content_type='application/json')
|
content_type='application/json')
|
||||||
recipe = json.loads(response.content)['recipe_json']
|
recipe = json.loads(response.content)['recipe_json']
|
||||||
validate_recipe(arg, recipe)
|
validate_recipe(arg, recipe)
|
||||||
|
|
||||||
|
|
||||||
|
# def test_description_replace_automation():
|
||||||
|
# if 'cookbook' in os.getcwd():
|
||||||
|
# test_file = os.path.join(os.getcwd(), 'other', 'test_data', 'chefkoch2.html')
|
||||||
|
# else:
|
||||||
|
# test_file = os.path.join(os.getcwd(), 'cookbook', 'tests', 'other', 'test_data', 'chefkoch2.html')
|
||||||
|
#
|
||||||
|
# with open(test_file, 'r', encoding='UTF-8') as d:
|
||||||
|
# pass
|
39
docs/features/automation.md
Normal file
39
docs/features/automation.md
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
!!! warning
|
||||||
|
Automations are currently in a beta stage. They work pretty stable but if I encounter any
|
||||||
|
issues while working on them, I might change how they work breaking existing automations.
|
||||||
|
I will try to avoid this and am pretty confident it won't happen.
|
||||||
|
|
||||||
|
|
||||||
|
Automations allow Tandoor to automatically perform certain tasks, especially when importing recipes, that
|
||||||
|
would otherwise have to be done manually. Currently, the following automations are supported.
|
||||||
|
|
||||||
|
|
||||||
|
## Unit, Food, Keyword Alias
|
||||||
|
asd
|
||||||
|
|
||||||
|
## Description Replace
|
||||||
|
This automation is a bit more complicated than the alis rules.
|
||||||
|
|
||||||
|
It uses Regular Expressions (RegEx) to determine if a description should be altered, what exactly to remove
|
||||||
|
and what to replace it with.
|
||||||
|
|
||||||
|
- **Parameter 1**: pattern of which sites to match (e.g. `.*.chefkoch.de.*`, `.*`)
|
||||||
|
- **Parameter 2**: pattern of what to replace (e.g. `.*`)
|
||||||
|
- **Parameter 3**: value to replace matched occurrence of parameter 2 with. Only one occurrence of the pattern is replaced.
|
||||||
|
|
||||||
|
To replace the description the python [re.sub](https://docs.python.org/2/library/re.html#re.sub) function is used
|
||||||
|
like this `re.sub(<parameter 2>, <parameter 2>, <descriotion>, count=1)`
|
||||||
|
|
||||||
|
To test out your patterns and learn about RegEx you can use [regexr.com](https://regexr.com/)
|
||||||
|
|
||||||
|
# Order
|
||||||
|
If the Automation type allows for more than one rule to be executed (for example description replace)
|
||||||
|
the rules are processed in ascending order (ordered by the *order* property of the automation).
|
||||||
|
The default order is always 1000 to make it easier to add automations before and after other automations.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
1. Rule ABC (order 1000) replaces `everything` with `abc`
|
||||||
|
2. Rule DEF (order 2000) replaces `everything` with `def`
|
||||||
|
3. Rule XYZ (order 500) replaces `everything` with `xyz`
|
||||||
|
|
||||||
|
After processing rules XYZ, then ABC and then DEF the description will have the value `def`
|
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
|
||||||
|
"spaces": 2,
|
||||||
|
"generator-cli": {
|
||||||
|
"version": "6.2.1"
|
||||||
|
}
|
||||||
|
}
|
@ -15,6 +15,7 @@
|
|||||||
<file-input v-if="visibleCondition(f, 'file')" :label="f.label" :value="f.value" :field="f.field" @change="storeValue" />
|
<file-input v-if="visibleCondition(f, 'file')" :label="f.label" :value="f.value" :field="f.field" @change="storeValue" />
|
||||||
<small-text v-if="visibleCondition(f, 'smalltext')" :value="f.value" />
|
<small-text v-if="visibleCondition(f, 'smalltext')" :value="f.value" />
|
||||||
<date-input v-if="visibleCondition(f, 'date')" :label="f.label" :value="f.value" :field="f.field" :help="showHelp && f.help" :subtitle="f.subtitle" />
|
<date-input v-if="visibleCondition(f, 'date')" :label="f.label" :value="f.value" :field="f.field" :help="showHelp && f.help" :subtitle="f.subtitle" />
|
||||||
|
<number-input v-if="visibleCondition(f, 'number')" :label="f.label" :value="f.value" :field="f.field" :placeholder="f.placeholder" :help="showHelp && f.help" :subtitle="f.subtitle" />
|
||||||
</div>
|
</div>
|
||||||
<template v-slot:modal-footer>
|
<template v-slot:modal-footer>
|
||||||
<div class="row w-100">
|
<div class="row w-100">
|
||||||
@ -49,10 +50,11 @@ import ChoiceInput from "@/components/Modals/ChoiceInput"
|
|||||||
import FileInput from "@/components/Modals/FileInput"
|
import FileInput from "@/components/Modals/FileInput"
|
||||||
import SmallText from "@/components/Modals/SmallText"
|
import SmallText from "@/components/Modals/SmallText"
|
||||||
import HelpBadge from "@/components/Badges/Help"
|
import HelpBadge from "@/components/Badges/Help"
|
||||||
|
import NumberInput from "@/components/Modals/NumberInput.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "GenericModalForm",
|
name: "GenericModalForm",
|
||||||
components: { FileInput, CheckboxInput, LookupInput, TextInput, EmojiInput, ChoiceInput, SmallText, HelpBadge,DateInput },
|
components: { FileInput, CheckboxInput, LookupInput, TextInput, EmojiInput, ChoiceInput, SmallText, HelpBadge,DateInput, NumberInput },
|
||||||
mixins: [ApiMixin, ToastMixin],
|
mixins: [ApiMixin, ToastMixin],
|
||||||
props: {
|
props: {
|
||||||
model: { required: true, type: Object },
|
model: { required: true, type: Object },
|
||||||
|
37
vue/src/components/Modals/NumberInput.vue
Normal file
37
vue/src/components/Modals/NumberInput.vue
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<b-form-group v-bind:label="label" class="mb-3">
|
||||||
|
<b-form-input v-model="new_value" type="number" :placeholder="placeholder"></b-form-input>
|
||||||
|
<em v-if="help" class="small text-muted">{{ help }}</em>
|
||||||
|
<small v-if="subtitle" class="text-muted">{{ subtitle }}</small>
|
||||||
|
</b-form-group>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "TextInput",
|
||||||
|
props: {
|
||||||
|
field: { type: String, default: "You Forgot To Set Field Name" },
|
||||||
|
label: { type: String, default: "Text Field" },
|
||||||
|
value: { type: String, default: "" },
|
||||||
|
placeholder: { type: Number, default: 0 },
|
||||||
|
help: { type: String, default: undefined },
|
||||||
|
subtitle: { type: String, default: undefined },
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
new_value: undefined,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.new_value = this.value
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
new_value: function () {
|
||||||
|
this.$root.$emit("change", this.field, this.new_value)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {},
|
||||||
|
}
|
||||||
|
</script>
|
File diff suppressed because it is too large
Load Diff
@ -137,6 +137,12 @@ export interface Automation {
|
|||||||
* @memberof Automation
|
* @memberof Automation
|
||||||
*/
|
*/
|
||||||
param_3?: string | null;
|
param_3?: string | null;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
* @memberof Automation
|
||||||
|
*/
|
||||||
|
order?: number;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
@ -158,7 +164,9 @@ export interface Automation {
|
|||||||
export enum AutomationTypeEnum {
|
export enum AutomationTypeEnum {
|
||||||
FoodAlias = 'FOOD_ALIAS',
|
FoodAlias = 'FOOD_ALIAS',
|
||||||
UnitAlias = 'UNIT_ALIAS',
|
UnitAlias = 'UNIT_ALIAS',
|
||||||
KeywordAlias = 'KEYWORD_ALIAS'
|
KeywordAlias = 'KEYWORD_ALIAS',
|
||||||
|
DescriptionReplace = 'DESCRIPTION_REPLACE',
|
||||||
|
InstructionReplace = 'INSTRUCTION_REPLACE'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -431,6 +439,12 @@ export interface Food {
|
|||||||
* @memberof Food
|
* @memberof Food
|
||||||
*/
|
*/
|
||||||
name: string;
|
name: string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof Food
|
||||||
|
*/
|
||||||
|
plural_name?: string | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
@ -655,6 +669,12 @@ export interface FoodSubstitute {
|
|||||||
* @memberof FoodSubstitute
|
* @memberof FoodSubstitute
|
||||||
*/
|
*/
|
||||||
name: string;
|
name: string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof FoodSubstitute
|
||||||
|
*/
|
||||||
|
plural_name?: string | null;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -848,10 +868,10 @@ export interface Ingredient {
|
|||||||
food: IngredientFood | null;
|
food: IngredientFood | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {FoodSupermarketCategory}
|
* @type {IngredientUnit}
|
||||||
* @memberof Ingredient
|
* @memberof Ingredient
|
||||||
*/
|
*/
|
||||||
unit: FoodSupermarketCategory | null;
|
unit: IngredientUnit | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
@ -894,6 +914,18 @@ export interface Ingredient {
|
|||||||
* @memberof Ingredient
|
* @memberof Ingredient
|
||||||
*/
|
*/
|
||||||
used_in_recipes?: string;
|
used_in_recipes?: string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof Ingredient
|
||||||
|
*/
|
||||||
|
always_use_plural_unit?: boolean;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof Ingredient
|
||||||
|
*/
|
||||||
|
always_use_plural_food?: boolean;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -913,6 +945,12 @@ export interface IngredientFood {
|
|||||||
* @memberof IngredientFood
|
* @memberof IngredientFood
|
||||||
*/
|
*/
|
||||||
name: string;
|
name: string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof IngredientFood
|
||||||
|
*/
|
||||||
|
plural_name?: string | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
@ -1004,6 +1042,37 @@ export interface IngredientFood {
|
|||||||
*/
|
*/
|
||||||
child_inherit_fields?: Array<FoodInheritFields> | null;
|
child_inherit_fields?: Array<FoodInheritFields> | null;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @interface IngredientUnit
|
||||||
|
*/
|
||||||
|
export interface IngredientUnit {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
* @memberof IngredientUnit
|
||||||
|
*/
|
||||||
|
id?: number;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof IngredientUnit
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof IngredientUnit
|
||||||
|
*/
|
||||||
|
plural_name?: string | null;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof IngredientUnit
|
||||||
|
*/
|
||||||
|
description?: string | null;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @export
|
* @export
|
||||||
@ -1746,13 +1815,13 @@ export interface MealPlanRecipe {
|
|||||||
* @type {string}
|
* @type {string}
|
||||||
* @memberof MealPlanRecipe
|
* @memberof MealPlanRecipe
|
||||||
*/
|
*/
|
||||||
rating?: string;
|
rating?: string | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
* @memberof MealPlanRecipe
|
* @memberof MealPlanRecipe
|
||||||
*/
|
*/
|
||||||
last_cooked?: string;
|
last_cooked?: string | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
@ -1953,13 +2022,13 @@ export interface Recipe {
|
|||||||
* @type {string}
|
* @type {string}
|
||||||
* @memberof Recipe
|
* @memberof Recipe
|
||||||
*/
|
*/
|
||||||
rating?: string;
|
rating?: string | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
* @memberof Recipe
|
* @memberof Recipe
|
||||||
*/
|
*/
|
||||||
last_cooked?: string;
|
last_cooked?: string | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
@ -2166,10 +2235,10 @@ export interface RecipeIngredients {
|
|||||||
food: IngredientFood | null;
|
food: IngredientFood | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {FoodSupermarketCategory}
|
* @type {IngredientUnit}
|
||||||
* @memberof RecipeIngredients
|
* @memberof RecipeIngredients
|
||||||
*/
|
*/
|
||||||
unit: FoodSupermarketCategory | null;
|
unit: IngredientUnit | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
@ -2212,6 +2281,18 @@ export interface RecipeIngredients {
|
|||||||
* @memberof RecipeIngredients
|
* @memberof RecipeIngredients
|
||||||
*/
|
*/
|
||||||
used_in_recipes?: string;
|
used_in_recipes?: string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof RecipeIngredients
|
||||||
|
*/
|
||||||
|
always_use_plural_unit?: boolean;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof RecipeIngredients
|
||||||
|
*/
|
||||||
|
always_use_plural_food?: boolean;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -2412,13 +2493,13 @@ export interface RecipeOverview {
|
|||||||
* @type {string}
|
* @type {string}
|
||||||
* @memberof RecipeOverview
|
* @memberof RecipeOverview
|
||||||
*/
|
*/
|
||||||
rating?: string;
|
rating?: string | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
* @memberof RecipeOverview
|
* @memberof RecipeOverview
|
||||||
*/
|
*/
|
||||||
last_cooked?: string;
|
last_cooked?: string | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
@ -2703,10 +2784,10 @@ export interface ShoppingListEntries {
|
|||||||
food: IngredientFood | null;
|
food: IngredientFood | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {FoodSupermarketCategory}
|
* @type {IngredientUnit}
|
||||||
* @memberof ShoppingListEntries
|
* @memberof ShoppingListEntries
|
||||||
*/
|
*/
|
||||||
unit?: FoodSupermarketCategory | null;
|
unit?: IngredientUnit | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {number}
|
* @type {number}
|
||||||
@ -2794,10 +2875,10 @@ export interface ShoppingListEntry {
|
|||||||
food: IngredientFood | null;
|
food: IngredientFood | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {FoodSupermarketCategory}
|
* @type {IngredientUnit}
|
||||||
* @memberof ShoppingListEntry
|
* @memberof ShoppingListEntry
|
||||||
*/
|
*/
|
||||||
unit?: FoodSupermarketCategory | null;
|
unit?: IngredientUnit | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {number}
|
* @type {number}
|
||||||
@ -3191,41 +3272,16 @@ export interface Space {
|
|||||||
file_size_mb?: string;
|
file_size_mb?: string;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {SpaceImage}
|
* @type {RecipeFile}
|
||||||
* @memberof Space
|
* @memberof Space
|
||||||
*/
|
*/
|
||||||
image?: SpaceImage;
|
image?: RecipeFile | null;
|
||||||
}
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @interface SpaceImage
|
|
||||||
*/
|
|
||||||
export interface SpaceImage {
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {number}
|
* @type {boolean}
|
||||||
* @memberof SpaceImage
|
* @memberof Space
|
||||||
*/
|
*/
|
||||||
id?: number;
|
use_plural?: boolean;
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @type {string}
|
|
||||||
* @memberof SpaceImage
|
|
||||||
*/
|
|
||||||
name: string;
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @type {string}
|
|
||||||
* @memberof SpaceImage
|
|
||||||
*/
|
|
||||||
file_download?: string;
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @type {string}
|
|
||||||
* @memberof SpaceImage
|
|
||||||
*/
|
|
||||||
preview?: string;
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -3563,6 +3619,12 @@ export interface Unit {
|
|||||||
* @memberof Unit
|
* @memberof Unit
|
||||||
*/
|
*/
|
||||||
name: string;
|
name: string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof Unit
|
||||||
|
*/
|
||||||
|
plural_name?: string | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
|
@ -717,4 +717,10 @@ export const formFunctions = {
|
|||||||
form.fields.filter((x) => x.field === "inherit_fields")[0].value = getUserPreference("food_inherit_default")
|
form.fields.filter((x) => x.field === "inherit_fields")[0].value = getUserPreference("food_inherit_default")
|
||||||
return form
|
return form
|
||||||
},
|
},
|
||||||
|
AutomationOrderDefault: function (form) {
|
||||||
|
if (form.fields.filter((x) => x.field === "order")[0].value === undefined) {
|
||||||
|
form.fields.filter((x) => x.field === "order")[0].value = 1000
|
||||||
|
}
|
||||||
|
return form
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user