basics
This commit is contained in:
parent
56252a707a
commit
6176eeb024
@ -28,7 +28,7 @@ class IngredientParser:
|
||||
self.food_aliases = c
|
||||
caches['default'].touch(FOOD_CACHE_KEY, 30)
|
||||
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
|
||||
caches['default'].set(FOOD_CACHE_KEY, self.food_aliases, 30)
|
||||
|
||||
@ -37,7 +37,7 @@ class IngredientParser:
|
||||
self.unit_aliases = c
|
||||
caches['default'].touch(UNIT_CACHE_KEY, 30)
|
||||
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
|
||||
caches['default'].set(UNIT_CACHE_KEY, self.unit_aliases, 30)
|
||||
else:
|
||||
@ -59,7 +59,7 @@ class IngredientParser:
|
||||
except KeyError:
|
||||
return food
|
||||
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 food
|
||||
|
||||
@ -78,7 +78,7 @@ class IngredientParser:
|
||||
except KeyError:
|
||||
return unit
|
||||
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 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.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
|
||||
|
||||
@ -121,7 +122,7 @@ def get_from_scraper(scrape, request):
|
||||
try:
|
||||
keywords.append(source_url.replace('http://', '').replace('https://', '').split('/')[0])
|
||||
except Exception:
|
||||
pass
|
||||
recipe_json['source_url'] = ''
|
||||
|
||||
try:
|
||||
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:
|
||||
recipe_json['steps'].append({'instruction': '', 'ingredients': [], })
|
||||
|
||||
if len(parse_description(description)) > 256: # split at 256 as long descriptions dont look good on recipe cards
|
||||
recipe_json['steps'][0]['instruction'] = f'*{parse_description(description)}* \n\n' + recipe_json['steps'][0]['instruction']
|
||||
parsed_description = parse_description(description)
|
||||
# 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:
|
||||
recipe_json['description'] = parse_description(description)[:512]
|
||||
recipe_json['description'] = parsed_description[:512]
|
||||
|
||||
try:
|
||||
for x in scrape.ingredients():
|
||||
@ -175,6 +184,12 @@ def get_from_scraper(scrape, request):
|
||||
except Exception:
|
||||
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
|
||||
|
||||
|
||||
|
@ -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)
|
||||
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)
|
||||
nav_color = models.CharField(choices=COLORS, max_length=128, default=PRIMARY)
|
||||
default_unit = models.CharField(max_length=32, default='g')
|
||||
@ -1223,9 +1223,12 @@ class Automation(ExportModelOperationsMixin('automations'), models.Model, Permis
|
||||
FOOD_ALIAS = 'FOOD_ALIAS'
|
||||
UNIT_ALIAS = 'UNIT_ALIAS'
|
||||
KEYWORD_ALIAS = 'KEYWORD_ALIAS'
|
||||
DESCRIPTION_REPLACE = 'DESCRIPTION_REPLACE'
|
||||
INSTRUCTION_REPLACE = 'INSTRUCTION_REPLACE'
|
||||
|
||||
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='')
|
||||
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_3 = models.CharField(max_length=128, blank=True, null=True)
|
||||
|
||||
order = models.IntegerField(default=1000)
|
||||
|
||||
disabled = models.BooleanField(default=False)
|
||||
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
@ -876,11 +876,11 @@ class ShoppingListRecipeSerializer(serializers.ModelSerializer):
|
||||
value = value.quantize(
|
||||
Decimal(1)) if value == value.to_integral() else value.normalize() # strips trailing zero
|
||||
return (
|
||||
obj.name
|
||||
or getattr(obj.mealplan, 'title', None)
|
||||
or (d := getattr(obj.mealplan, 'date', None)) and ': '.join([obj.mealplan.recipe.name, str(d)])
|
||||
or obj.recipe.name
|
||||
) + f' ({value:.2g})'
|
||||
obj.name
|
||||
or getattr(obj.mealplan, 'title', None)
|
||||
or (d := getattr(obj.mealplan, 'date', None)) and ': '.join([obj.mealplan.recipe.name, str(d)])
|
||||
or obj.recipe.name
|
||||
) + f' ({value:.2g})'
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
# TODO remove once old shopping list
|
||||
@ -1067,7 +1067,7 @@ class AutomationSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Automation
|
||||
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',)
|
||||
|
||||
|
||||
|
@ -72,3 +72,13 @@ def test_recipe_import(arg, u1_s1):
|
||||
content_type='application/json')
|
||||
recipe = json.loads(response.content)['recipe_json']
|
||||
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" />
|
||||
<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" />
|
||||
<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>
|
||||
<template v-slot:modal-footer>
|
||||
<div class="row w-100">
|
||||
@ -49,10 +50,11 @@ import ChoiceInput from "@/components/Modals/ChoiceInput"
|
||||
import FileInput from "@/components/Modals/FileInput"
|
||||
import SmallText from "@/components/Modals/SmallText"
|
||||
import HelpBadge from "@/components/Badges/Help"
|
||||
import NumberInput from "@/components/Modals/NumberInput.vue";
|
||||
|
||||
export default {
|
||||
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],
|
||||
props: {
|
||||
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
|
||||
*/
|
||||
param_3?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof Automation
|
||||
*/
|
||||
order?: number;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
@ -158,7 +164,9 @@ export interface Automation {
|
||||
export enum AutomationTypeEnum {
|
||||
FoodAlias = 'FOOD_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
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof Food
|
||||
*/
|
||||
plural_name?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
@ -655,6 +669,12 @@ export interface FoodSubstitute {
|
||||
* @memberof FoodSubstitute
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof FoodSubstitute
|
||||
*/
|
||||
plural_name?: string | null;
|
||||
}
|
||||
/**
|
||||
*
|
||||
@ -848,10 +868,10 @@ export interface Ingredient {
|
||||
food: IngredientFood | null;
|
||||
/**
|
||||
*
|
||||
* @type {FoodSupermarketCategory}
|
||||
* @type {IngredientUnit}
|
||||
* @memberof Ingredient
|
||||
*/
|
||||
unit: FoodSupermarketCategory | null;
|
||||
unit: IngredientUnit | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
@ -894,6 +914,18 @@ export interface Ingredient {
|
||||
* @memberof Ingredient
|
||||
*/
|
||||
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
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof IngredientFood
|
||||
*/
|
||||
plural_name?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
@ -1004,6 +1042,37 @@ export interface IngredientFood {
|
||||
*/
|
||||
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
|
||||
@ -1746,13 +1815,13 @@ export interface MealPlanRecipe {
|
||||
* @type {string}
|
||||
* @memberof MealPlanRecipe
|
||||
*/
|
||||
rating?: string;
|
||||
rating?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof MealPlanRecipe
|
||||
*/
|
||||
last_cooked?: string;
|
||||
last_cooked?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
@ -1953,13 +2022,13 @@ export interface Recipe {
|
||||
* @type {string}
|
||||
* @memberof Recipe
|
||||
*/
|
||||
rating?: string;
|
||||
rating?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof Recipe
|
||||
*/
|
||||
last_cooked?: string;
|
||||
last_cooked?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
@ -2166,10 +2235,10 @@ export interface RecipeIngredients {
|
||||
food: IngredientFood | null;
|
||||
/**
|
||||
*
|
||||
* @type {FoodSupermarketCategory}
|
||||
* @type {IngredientUnit}
|
||||
* @memberof RecipeIngredients
|
||||
*/
|
||||
unit: FoodSupermarketCategory | null;
|
||||
unit: IngredientUnit | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
@ -2212,6 +2281,18 @@ export interface RecipeIngredients {
|
||||
* @memberof RecipeIngredients
|
||||
*/
|
||||
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}
|
||||
* @memberof RecipeOverview
|
||||
*/
|
||||
rating?: string;
|
||||
rating?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof RecipeOverview
|
||||
*/
|
||||
last_cooked?: string;
|
||||
last_cooked?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
@ -2703,10 +2784,10 @@ export interface ShoppingListEntries {
|
||||
food: IngredientFood | null;
|
||||
/**
|
||||
*
|
||||
* @type {FoodSupermarketCategory}
|
||||
* @type {IngredientUnit}
|
||||
* @memberof ShoppingListEntries
|
||||
*/
|
||||
unit?: FoodSupermarketCategory | null;
|
||||
unit?: IngredientUnit | null;
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
@ -2794,10 +2875,10 @@ export interface ShoppingListEntry {
|
||||
food: IngredientFood | null;
|
||||
/**
|
||||
*
|
||||
* @type {FoodSupermarketCategory}
|
||||
* @type {IngredientUnit}
|
||||
* @memberof ShoppingListEntry
|
||||
*/
|
||||
unit?: FoodSupermarketCategory | null;
|
||||
unit?: IngredientUnit | null;
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
@ -3191,41 +3272,16 @@ export interface Space {
|
||||
file_size_mb?: string;
|
||||
/**
|
||||
*
|
||||
* @type {SpaceImage}
|
||||
* @type {RecipeFile}
|
||||
* @memberof Space
|
||||
*/
|
||||
image?: SpaceImage;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface SpaceImage
|
||||
*/
|
||||
export interface SpaceImage {
|
||||
image?: RecipeFile | null;
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof SpaceImage
|
||||
* @type {boolean}
|
||||
* @memberof Space
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof SpaceImage
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof SpaceImage
|
||||
*/
|
||||
file_download?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof SpaceImage
|
||||
*/
|
||||
preview?: string;
|
||||
use_plural?: boolean;
|
||||
}
|
||||
/**
|
||||
*
|
||||
@ -3563,6 +3619,12 @@ export interface Unit {
|
||||
* @memberof Unit
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof Unit
|
||||
*/
|
||||
plural_name?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
|
@ -717,4 +717,10 @@ export const formFunctions = {
|
||||
form.fields.filter((x) => x.field === "inherit_fields")[0].value = getUserPreference("food_inherit_default")
|
||||
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