change ingore_inherit to inherit_fields

This commit is contained in:
smilerz 2021-12-30 12:54:39 -06:00
parent 3fafd43e58
commit 79b4bc387e
18 changed files with 190 additions and 248 deletions

View File

@ -7,7 +7,7 @@ from django_scopes import scopes_disabled
from django_scopes.forms import SafeModelChoiceField, SafeModelMultipleChoiceField from django_scopes.forms import SafeModelChoiceField, SafeModelMultipleChoiceField
from hcaptcha.fields import hCaptchaField from hcaptcha.fields import hCaptchaField
from .models import (Comment, InviteLink, Keyword, MealPlan, MealType, Recipe, RecipeBook, Food, from .models import (Comment, Food, InviteLink, Keyword, MealPlan, MealType, Recipe, RecipeBook,
RecipeBookEntry, SearchPreference, Space, Storage, Sync, User, UserPreference) RecipeBookEntry, SearchPreference, Space, Storage, Sync, User, UserPreference)
@ -525,7 +525,7 @@ class SpacePreferenceForm(forms.ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) # populates the post super().__init__(*args, **kwargs) # populates the post
self.fields['food_inherit'].queryset = Food.inherit_fields self.fields['food_inherit'].queryset = Food.inheritable_fields
class Meta: class Meta:
model = Space model = Space

View File

@ -28,11 +28,6 @@ class Migration(migrations.Migration):
] ]
operations = [ operations = [
# migrations.AddField(
# model_name='food',
# name='on_hand',
# field=models.BooleanField(default=False),
# ),
migrations.AddField( migrations.AddField(
model_name='shoppinglistentry', model_name='shoppinglistentry',
name='completed_at', name='completed_at',
@ -105,11 +100,6 @@ class Migration(migrations.Migration):
], ],
bases=(models.Model, PermissionModelMixin), bases=(models.Model, PermissionModelMixin),
), ),
migrations.AddField(
model_name='food',
name='inherit',
field=models.BooleanField(default=False),
),
migrations.AddField( migrations.AddField(
model_name='userpreference', model_name='userpreference',
name='mealplan_autoinclude_related', name='mealplan_autoinclude_related',
@ -117,7 +107,7 @@ class Migration(migrations.Migration):
), ),
migrations.AddField( migrations.AddField(
model_name='food', model_name='food',
name='ignore_inherit', name='inherit_fields',
field=models.ManyToManyField(blank=True, to='cookbook.FoodInheritField'), field=models.ManyToManyField(blank=True, to='cookbook.FoodInheritField'),
), ),
migrations.AddField( migrations.AddField(
@ -145,5 +135,10 @@ class Migration(migrations.Migration):
name='shopping_recent_days', name='shopping_recent_days',
field=models.PositiveIntegerField(default=7), field=models.PositiveIntegerField(default=7),
), ),
migrations.RenameField(
model_name='food',
old_name='ignore_shopping',
new_name='food_onhand',
),
migrations.RunPython(copy_values_to_sle), migrations.RunPython(copy_values_to_sle),
] ]

View File

@ -1,35 +0,0 @@
# Generated by Django 3.2.10 on 2021-12-29 20:11
from django.db import migrations
from cookbook.models import FoodInheritField
# TODO this can be deleted
# def temp_migration(apps, schema_editor):
# x = FoodInheritField.objects.get(name='Ignore Shopping', field='ignore_shopping')
# x.name = 'On Hand'
# x.field = 'food_onhand'
# x.save
class Migration(migrations.Migration):
dependencies = [
('cookbook', '0162_userpreference_csv_delim'),
]
operations = [
# TODO this can be deleted
# migrations.RunPython(temp_migration),
# TODO this stays
migrations.RenameField(
model_name='food',
old_name='ignore_shopping',
new_name='food_onhand',
),
# TODO this can be deleted
# migrations.RemoveField(
# model_name='food',
# name='on_hand',
# ),
]

View File

@ -482,7 +482,7 @@ class Unit(ExportModelOperationsMixin('unit'), models.Model, PermissionModelMixi
class Food(ExportModelOperationsMixin('food'), TreeModel, PermissionModelMixin): class Food(ExportModelOperationsMixin('food'), TreeModel, PermissionModelMixin):
# exclude fields not implemented yet # exclude fields not implemented yet
inherit_fields = FoodInheritField.objects.exclude(field__in=['diet', 'substitute', 'substitute_children', 'substitute_siblings']) inheritable_fields = FoodInheritField.objects.exclude(field__in=['diet', 'substitute', 'substitute_children', 'substitute_siblings'])
# WARNING: Food inheritance relies on post_save signals, avoid using UPDATE to update Food objects unless you intend to bypass those signals # WARNING: Food inheritance relies on post_save signals, avoid using UPDATE to update Food objects unless you intend to bypass those signals
if SORT_TREE_BY_NAME: if SORT_TREE_BY_NAME:
@ -492,9 +492,7 @@ class Food(ExportModelOperationsMixin('food'), TreeModel, PermissionModelMixin):
supermarket_category = models.ForeignKey(SupermarketCategory, null=True, blank=True, on_delete=models.SET_NULL) supermarket_category = models.ForeignKey(SupermarketCategory, null=True, blank=True, on_delete=models.SET_NULL)
food_onhand = models.BooleanField(default=False) # inherited field food_onhand = models.BooleanField(default=False) # inherited field
description = models.TextField(default='', blank=True) description = models.TextField(default='', blank=True)
# on_hand = models.BooleanField(default=False) inherit_fields = models.ManyToManyField(FoodInheritField, blank=True) # inherited field: is this name better as inherit instead of ignore inherit? which is more intuitive?
inherit = models.BooleanField(default=False)
ignore_inherit = models.ManyToManyField(FoodInheritField, blank=True) # inherited field: is this name better as inherit instead of ignore inherit? which is more intuitive?
space = models.ForeignKey(Space, on_delete=models.CASCADE) space = models.ForeignKey(Space, on_delete=models.CASCADE)
objects = ScopedManager(space='space', _manager_class=TreeManager) objects = ScopedManager(space='space', _manager_class=TreeManager)
@ -512,16 +510,14 @@ class Food(ExportModelOperationsMixin('food'), TreeModel, PermissionModelMixin):
def reset_inheritance(space=None): def reset_inheritance(space=None):
# resets inheritted fields to the space defaults and updates all inheritted fields to root object values # resets inheritted fields to the space defaults and updates all inheritted fields to root object values
inherit = space.food_inherit.all() inherit = space.food_inherit.all()
ignore_inherit = Food.inherit_fields.difference(inherit)
# remove all inherited fields from food
Through = Food.objects.filter(space=space).first().inherit_fields.through
Through.objects.all().delete()
# food is going to inherit attributes # food is going to inherit attributes
if space.food_inherit.all().count() > 0: if space.food_inherit.all().count() > 0:
# using update to avoid creating a N*depth! save signals
Food.objects.filter(space=space).update(inherit=True)
# ManyToMany cannot be updated through an UPDATE operation # ManyToMany cannot be updated through an UPDATE operation
Through = Food.objects.first().ignore_inherit.through for i in inherit:
Through.objects.all().delete()
for i in ignore_inherit:
Through.objects.bulk_create([ Through.objects.bulk_create([
Through(food_id=x, foodinheritfield_id=i.id) Through(food_id=x, foodinheritfield_id=i.id)
for x in Food.objects.filter(space=space).values_list('id', flat=True) for x in Food.objects.filter(space=space).values_list('id', flat=True)
@ -538,8 +534,6 @@ class Food(ExportModelOperationsMixin('food'), TreeModel, PermissionModelMixin):
category_roots = Food.exclude_descendants(queryset=Food.objects.filter(supermarket_category__isnull=False, numchild__gt=0, space=space)) category_roots = Food.exclude_descendants(queryset=Food.objects.filter(supermarket_category__isnull=False, numchild__gt=0, space=space))
for root in category_roots: for root in category_roots:
root.get_descendants().update(supermarket_category=root.supermarket_category) root.get_descendants().update(supermarket_category=root.supermarket_category)
else: # food is not going to inherit any attributes
Food.objects.filter(space=space).update(inherit=False)
class Meta: class Meta:
constraints = [ constraints = [

View File

@ -157,15 +157,9 @@ class FoodInheritFieldSerializer(WritableNestedModelSerializer):
class UserPreferenceSerializer(serializers.ModelSerializer): class UserPreferenceSerializer(serializers.ModelSerializer):
# food_inherit_default = FoodInheritFieldSerializer(source='space.food_inherit', read_only=True) food_inherit_default = FoodInheritFieldSerializer(source='space.food_inherit', many=True, allow_null=True, required=False, read_only=True)
food_ignore_default = serializers.SerializerMethodField('get_ignore_default')
plan_share = UserNameSerializer(many=True, allow_null=True, required=False, read_only=True) plan_share = UserNameSerializer(many=True, allow_null=True, required=False, read_only=True)
# TODO decide: default inherit field values for foods are being handled via VUE client through user preference
# should inherit field instead be set during the django model create?
def get_ignore_default(self, obj):
return FoodInheritFieldSerializer(Food.inherit_fields.difference(obj.space.food_inherit.all()), many=True).data
def create(self, validated_data): def create(self, validated_data):
if not validated_data.get('user', None): if not validated_data.get('user', None):
raise ValidationError(_('A user is required')) raise ValidationError(_('A user is required'))
@ -181,7 +175,7 @@ class UserPreferenceSerializer(serializers.ModelSerializer):
model = UserPreference model = UserPreference
fields = ( fields = (
'user', 'theme', 'nav_color', 'default_unit', 'default_page', 'use_kj', 'search_style', 'show_recent', 'plan_share', 'user', 'theme', 'nav_color', 'default_unit', 'default_page', 'use_kj', 'search_style', 'show_recent', 'plan_share',
'ingredient_decimals', 'comments', 'shopping_auto_sync', 'mealplan_autoadd_shopping', 'food_ignore_default', 'default_delay', 'ingredient_decimals', 'comments', 'shopping_auto_sync', 'mealplan_autoadd_shopping', 'food_inherit_default', 'default_delay',
'mealplan_autoinclude_related', 'mealplan_autoexclude_onhand', 'shopping_share', 'shopping_recent_days', 'csv_delim', 'csv_prefix', 'filter_to_supermarket' 'mealplan_autoinclude_related', 'mealplan_autoexclude_onhand', 'shopping_share', 'shopping_recent_days', 'csv_delim', 'csv_prefix', 'filter_to_supermarket'
) )
@ -376,7 +370,7 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR
supermarket_category = SupermarketCategorySerializer(allow_null=True, required=False) supermarket_category = SupermarketCategorySerializer(allow_null=True, required=False)
recipe = RecipeSimpleSerializer(allow_null=True, required=False) recipe = RecipeSimpleSerializer(allow_null=True, required=False)
shopping = serializers.SerializerMethodField('get_shopping_status') shopping = serializers.SerializerMethodField('get_shopping_status')
ignore_inherit = FoodInheritFieldSerializer(many=True, allow_null=True, required=False) inherit_fields = FoodInheritFieldSerializer(many=True, allow_null=True, required=False)
recipe_filter = 'steps__ingredients__food' recipe_filter = 'steps__ingredients__food'
@ -403,7 +397,7 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR
model = Food model = Food
fields = ( fields = (
'id', 'name', 'description', 'shopping', 'recipe', 'food_onhand', 'supermarket_category', 'id', 'name', 'description', 'shopping', 'recipe', 'food_onhand', 'supermarket_category',
'image', 'parent', 'numchild', 'numrecipe', 'inherit', 'ignore_inherit', 'full_name' 'image', 'parent', 'numchild', 'numrecipe', 'inherit_fields', 'full_name'
) )
read_only_fields = ('id', 'numchild', 'parent', 'image', 'numrecipe') read_only_fields = ('id', 'numchild', 'parent', 'image', 'numrecipe')

View File

@ -66,14 +66,14 @@ def update_food_inheritance(sender, instance=None, created=False, **kwargs):
if not instance: if not instance:
return return
inherit = Food.inherit_fields.difference(instance.ignore_inherit.all()) inherit = instance.inherit_fields.all()
# nothing to apply from parent and nothing to apply to children # nothing to apply from parent and nothing to apply to children
if (not instance.inherit or not instance.parent or inherit.count() == 0) and instance.numchild == 0: if (not instance.parent or inherit.count() == 0) and instance.numchild == 0:
return return
inherit = inherit.values_list('field', flat=True) inherit = inherit.values_list('field', flat=True)
# apply changes from parent to instance for each inheritted field # apply changes from parent to instance for each inheritted field
if instance.inherit and instance.parent and inherit.count() > 0: if instance.parent and inherit.count() > 0:
parent = instance.get_parent() parent = instance.get_parent()
if 'food_onhand' in inherit: if 'food_onhand' in inherit:
instance.food_onhand = parent.food_onhand instance.food_onhand = parent.food_onhand
@ -89,13 +89,13 @@ def update_food_inheritance(sender, instance=None, created=False, **kwargs):
# TODO figure out how to generalize this # TODO figure out how to generalize this
# apply changes to direct children - depend on save signals for those objects to cascade inheritance down # apply changes to direct children - depend on save signals for those objects to cascade inheritance down
_save = [] _save = []
for child in instance.get_children().filter(inherit=True).exclude(ignore_inherit__field='food_onhand'): for child in instance.get_children().filter(inherit_fields__field='food_onhand'):
child.food_onhand = instance.food_onhand child.food_onhand = instance.food_onhand
_save.append(child) _save.append(child)
# don't cascade empty supermarket category # don't cascade empty supermarket category
if instance.supermarket_category: if instance.supermarket_category:
# apply changes to direct children - depend on save signals for those objects to cascade inheritance down # apply changes to direct children - depend on save signals for those objects to cascade inheritance down
for child in instance.get_children().filter(inherit=True).exclude(ignore_inherit__field='supermarket_category'): for child in instance.get_children().filter(inherit_fields__field='supermarket_category'):
child.supermarket_category = instance.supermarket_category child.supermarket_category = instance.supermarket_category
_save.append(child) _save.append(child)
for child in set(_save): for child in set(_save):

View File

@ -57,7 +57,19 @@ def obj_tree_1(request, space_1):
except AttributeError: except AttributeError:
params = {} params = {}
objs = [] objs = []
inherit = params.pop('inherit', False)
objs.extend(FoodFactory.create_batch(3, space=space_1, **params)) objs.extend(FoodFactory.create_batch(3, space=space_1, **params))
# set all foods to inherit everything
if inherit:
inherit = Food.inheritable_fields
Through = Food.objects.filter(space=space_1).first().inherit_fields.through
for i in inherit:
Through.objects.bulk_create([
Through(food_id=x, foodinheritfield_id=i.id)
for x in Food.objects.filter(space=space_1).values_list('id', flat=True)
])
objs[0].move(objs[1], node_location) objs[0].move(objs[1], node_location)
objs[1].move(objs[2], node_location) objs[1].move(objs[2], node_location)
return Food.objects.get(id=objs[1].id) # whenever you move/merge a tree it's safest to re-get the object return Food.objects.get(id=objs[1].id) # whenever you move/merge a tree it's safest to re-get the object
@ -496,42 +508,12 @@ def test_inherit(request, obj_tree_1, field, inherit, new_val, u1_s1):
assert (getattr(child, field) == new_val) == inherit assert (getattr(child, field) == new_val) == inherit
@pytest.mark.parametrize("obj_tree_1, field, inherit, new_val", [
({'has_category': True, 'inherit': True, }, 'supermarket_category', True, 'cat_1'),
({'food_onhand': True, 'inherit': True, }, 'food_onhand', True, 'false'),
], indirect=['obj_tree_1'])
# This is more about the model than the API - should this be moved to a different test?
def test_ignoreinherit_field(request, obj_tree_1, field, inherit, new_val, u1_s1):
with scope(space=obj_tree_1.space):
parent = obj_tree_1.get_parent()
child = obj_tree_1.get_descendants()[0]
obj_tree_1.ignore_inherit.add(FoodInheritField.objects.get(field=field))
new_val = request.getfixturevalue(new_val)
# change parent to a new value
setattr(parent, field, new_val)
with scope(space=parent.space):
parent.save() # trigger post-save signal
# get the objects again because values are cached
obj_tree_1 = Food.objects.get(id=obj_tree_1.id)
# inheritance is blocked - should not get new value
assert getattr(obj_tree_1, field) != new_val
setattr(obj_tree_1, field, new_val)
with scope(space=parent.space):
obj_tree_1.save() # trigger post-save signal
# get the objects again because values are cached
child = Food.objects.get(id=child.id)
# inherit with child should still work
assert getattr(child, field) == new_val
@pytest.mark.parametrize("obj_tree_1", [ @pytest.mark.parametrize("obj_tree_1", [
({'has_category': True, 'inherit': False, 'food_onhand': True}), ({'has_category': True, 'inherit': False, 'food_onhand': True}),
], indirect=['obj_tree_1']) ], indirect=['obj_tree_1'])
def test_reset_inherit(obj_tree_1, space_1): def test_reset_inherit(obj_tree_1, space_1):
with scope(space=space_1): with scope(space=space_1):
space_1.food_inherit.add(*Food.inherit_fields.values_list('id', flat=True)) # set default inherit fields space_1.food_inherit.add(*Food.inheritable_fields.values_list('id', flat=True)) # set default inherit fields
parent = obj_tree_1.get_parent() parent = obj_tree_1.get_parent()
child = obj_tree_1.get_descendants()[0] child = obj_tree_1.get_descendants()[0]
obj_tree_1.food_onhand = False obj_tree_1.food_onhand = False

View File

@ -112,30 +112,29 @@ def test_preference_delete(u1_s1, u2_s1):
def test_default_inherit_fields(u1_s1, u1_s2, space_1, space_2): def test_default_inherit_fields(u1_s1, u1_s2, space_1, space_2):
food_inherit_fields = Food.inherit_fields.all() food_inherit_fields = Food.inheritable_fields
assert len([x.field for x in food_inherit_fields]) > 0
r = u1_s1.get(
reverse(DETAIL_URL, args={auth.get_user(u1_s1).id}),
)
# by default space food will not inherit any fields, so all of them will be ignored # by default space food will not inherit any fields, so all of them will be ignored
assert space_1.food_inherit.all().count() == 0 assert space_1.food_inherit.all().count() == 0
assert len([x.field for x in food_inherit_fields]) == len([x['field'] for x in json.loads(r.content)['food_ignore_default']]) > 0
# inherit all possible fields
with scope(space=space_1):
space_1.food_inherit.add(*Food.inherit_fields.values_list('id', flat=True))
r = u1_s1.get( r = u1_s1.get(
reverse(DETAIL_URL, args={auth.get_user(u1_s1).id}), reverse(DETAIL_URL, args={auth.get_user(u1_s1).id}),
) )
assert len([x['field'] for x in json.loads(r.content)['food_inherit_default']]) == 0
assert space_1.food_inherit.all().count() == Food.inherit_fields.all().count() > 0 # inherit all possible fields
# now by default, food is not ignoring inheritance on any field with scope(space=space_1):
assert len([x['field'] for x in json.loads(r.content)['food_ignore_default']]) == 0 space_1.food_inherit.add(*Food.inheritable_fields.values_list('id', flat=True))
# other spaces and users in those spaced not effected assert space_1.food_inherit.all().count() == Food.inheritable_fields.count() > 0
# now by default, food is inheriting all of the possible fields
r = u1_s1.get(
reverse(DETAIL_URL, args={auth.get_user(u1_s1).id}),
)
assert len([x['field'] for x in json.loads(r.content)['food_inherit_default']]) == space_1.food_inherit.all().count()
# other spaces and users in those spaces not effected
r = u1_s2.get( r = u1_s2.get(
reverse(DETAIL_URL, args={auth.get_user(u1_s2).id}), reverse(DETAIL_URL, args={auth.get_user(u1_s2).id}),
) )
assert space_2.food_inherit.all().count() == 0 assert space_2.food_inherit.all().count() == 0 == len([x['field'] for x in json.loads(r.content)['food_inherit_default']])
assert len([x.field for x in food_inherit_fields]) == len([x['field'] for x in json.loads(r.content)['food_ignore_default']]) > 0

View File

@ -402,7 +402,8 @@ class FoodInheritFieldViewSet(viewsets.ReadOnlyModelViewSet):
def get_queryset(self): def get_queryset(self):
# exclude fields not yet implemented # exclude fields not yet implemented
return Food.inherit_fields self.queryset = Food.inheritable_fields
return super().get_queryset()
class FoodViewSet(viewsets.ModelViewSet, TreeMixin): class FoodViewSet(viewsets.ModelViewSet, TreeMixin):

View File

@ -561,7 +561,7 @@ def space(request):
space_form = SpacePreferenceForm(instance=request.space) space_form = SpacePreferenceForm(instance=request.space)
space_form.base_fields['food_inherit'].queryset = Food.inherit_fields space_form.base_fields['food_inherit'].queryset = Food.inheritable_fields
if request.method == "POST" and 'space_form' in request.POST: if request.method == "POST" and 'space_form' in request.POST:
form = SpacePreferenceForm(request.POST, prefix='space') form = SpacePreferenceForm(request.POST, prefix='space')
if form.is_valid(): if form.is_valid():

View File

@ -1012,8 +1012,8 @@ export default {
let api = new ApiApiFactory() let api = new ApiApiFactory()
let ignore_category let ignore_category
if (field) { if (field) {
ignore_category = food.ignore_inherit ignore_category = food.inherit_fields
.map((x) => food.ignore_inherit.fields) .map((x) => food.inherit_fields.fields)
.flat() .flat()
.includes(field) .includes(field)
} else { } else {
@ -1023,7 +1023,7 @@ export default {
return api return api
.partialUpdateFood(food.id, food) .partialUpdateFood(food.id, food)
.then((result) => { .then((result) => {
if (food.inherit && food.supermarket_category && !ignore_category && food.parent) { if (food.supermarket_category && !ignore_category && food.parent) {
makeToast(this.$t("Warning"), this.$t("InheritWarning", { food: food.name }), "warning") makeToast(this.$t("Warning"), this.$t("InheritWarning", { food: food.name }), "warning")
} else { } else {
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_UPDATE) StandardToasts.makeStandardToast(StandardToasts.SUCCESS_UPDATE)

View File

@ -8,13 +8,13 @@
:class="[shopping ? 'text-success fa-shopping-cart' : 'text-muted fa-cart-plus']" :class="[shopping ? 'text-success fa-shopping-cart' : 'text-muted fa-cart-plus']"
/> />
</b-button> </b-button>
<b-popover :target="`${ShowConfirmation}`" :ref="'shopping' + item.id" triggers="focus" placement="top"> <b-popover v-if="shopping" :target="`${ShowConfirmation}`" :ref="'shopping' + item.id" triggers="focus" placement="top">
<template #title>{{ DeleteConfirmation }}</template> <template #title>{{ DeleteConfirmation }}</template>
<b-row align-h="end"> <b-row align-h="end">
<b-col cols="auto" <b-col cols="auto">
><b-button class="btn btn-sm btn-info shadow-none px-1 border-0" @click="cancelDelete()">{{ $t("Cancel") }}</b-button> <b-button class="btn btn-sm btn-info shadow-none px-1 border-0" @click="cancelDelete()">{{ $t("Cancel") }}</b-button>
<b-button class="btn btn-sm btn-danger shadow-none px-1" @click="confirmDelete()">{{ $t("Confirm") }}</b-button></b-col <b-button class="btn btn-sm btn-danger shadow-none px-1" @click="confirmDelete()">{{ $t("Confirm") }}</b-button>
> </b-col>
</b-row> </b-row>
</b-popover> </b-popover>
</span> </span>
@ -46,7 +46,7 @@ export default {
if (this.shopping) { if (this.shopping) {
return "shopping" + this.item.id return "shopping" + this.item.id
} else { } else {
return "NoDialog" return ""
} }
}, },
}, },

View File

@ -1,20 +1,18 @@
<template> <template>
<div> <div>
<b-modal :id="'modal_' + id" @hidden="cancelAction"> <b-modal :id="'modal_' + id" @hidden="cancelAction">
<template v-slot:modal-title <template v-slot:modal-title>
><h4>{{ form.title }}</h4></template <h4>{{ form.title }}</h4>
> </template>
<div v-for="(f, i) in form.fields" v-bind:key="i"> <div v-for="(f, i) in form.fields" v-bind:key="i">
<p v-if="f.type == 'instruction'">{{ f.label }}</p> <p v-if="visibleCondition(f, 'instruction')">{{ f.label }}</p>
<!-- this lookup is single selection --> <lookup-input v-if="visibleCondition(f, 'lookup')" :form="f" :model="listModel(f.list)" @change="storeValue" />
<lookup-input v-if="f.type == 'lookup'" :form="f" :model="listModel(f.list)" @change="storeValue" /> <checkbox-input class="mb-3" v-if="visibleCondition(f, 'checkbox')" :label="f.label" :value="f.value" :field="f.field" />
<!-- TODO: add multi-selection input list --> <text-input v-if="visibleCondition(f, 'text')" :label="f.label" :value="f.value" :field="f.field" :placeholder="f.placeholder" />
<checkbox-input v-if="f.type == 'checkbox'" :label="f.label" :value="f.value" :field="f.field" /> <choice-input v-if="visibleCondition(f, 'choice')" :label="f.label" :value="f.value" :field="f.field" :options="f.options" :placeholder="f.placeholder" />
<text-input v-if="f.type == 'text'" :label="f.label" :value="f.value" :field="f.field" :placeholder="f.placeholder" /> <emoji-input v-if="visibleCondition(f, 'emoji')" :label="f.label" :value="f.value" :field="f.field" @change="storeValue" />
<choice-input v-if="f.type == 'choice'" :label="f.label" :value="f.value" :field="f.field" :options="f.options" :placeholder="f.placeholder" /> <file-input v-if="visibleCondition(f, 'file')" :label="f.label" :value="f.value" :field="f.field" @change="storeValue" />
<emoji-input v-if="f.type == 'emoji'" :label="f.label" :value="f.value" :field="f.field" @change="storeValue" /> <small-text v-if="visibleCondition(f, 'smalltext')" :value="f.value" />
<file-input v-if="f.type == 'file'" :label="f.label" :value="f.value" :field="f.field" @change="storeValue" />
<small-text v-if="f.type == 'smalltext'" :value="f.value" />
</div> </div>
<template v-slot:modal-footer> <template v-slot:modal-footer>
@ -48,7 +46,12 @@ export default {
mixins: [ApiMixin, ToastMixin], mixins: [ApiMixin, ToastMixin],
props: { props: {
model: { required: true, type: Object }, model: { required: true, type: Object },
action: { type: Object }, action: {
type: Object,
default() {
return {}
},
},
item1: { item1: {
type: Object, type: Object,
default() { default() {
@ -249,6 +252,21 @@ export default {
apiClient.createAutomation(automation) apiClient.createAutomation(automation)
} }
}, },
visibleCondition(field, field_type) {
let type_match = field?.type == field_type
let checks = true
if (type_match && field?.condition) {
if (field.condition?.condition === "exists") {
if ((this.item1[field.condition.field] != undefined) === field.condition.value) {
checks = true
} else {
checks = false
}
}
}
return type_match && checks
},
}, },
} }
</script> </script>

View File

@ -1,26 +1,19 @@
<template> <template>
<div> <div>
<b-form-group <b-form-group v-bind:label="label" class="mb-3">
v-bind:label="label" <b-form-input v-model="new_value" type="text" :placeholder="placeholder"></b-form-input>
class="mb-3">
<b-form-input
v-model="new_value"
type="string"
:placeholder="placeholder"
></b-form-input>
</b-form-group> </b-form-group>
</div> </div>
</template> </template>
<script> <script>
export default { export default {
name: 'TextInput', name: "TextInput",
props: { props: {
field: {type: String, default: 'You Forgot To Set Field Name'}, field: { type: String, default: "You Forgot To Set Field Name" },
label: {type: String, default: 'Text Field'}, label: { type: String, default: "Text Field" },
value: {type: String, default: ''}, value: { type: String, default: "" },
placeholder: {type: String, default: 'You Should Add Placeholder Text'}, placeholder: { type: String, default: "You Should Add Placeholder Text" },
show_merge: { type: Boolean, default: false }, show_merge: { type: Boolean, default: false },
}, },
data() { data() {
@ -32,11 +25,10 @@ export default {
this.new_value = this.value this.new_value = this.value
}, },
watch: { watch: {
'new_value': function () { new_value: function () {
this.$root.$emit('change', this.field, this.new_value) this.$root.$emit("change", this.field, this.new_value)
}, },
}, },
methods: { methods: {},
}
} }
</script> </script>

View File

@ -222,7 +222,7 @@
"Next_Day": "Next Day", "Next_Day": "Next Day",
"Previous_Day": "Previous Day", "Previous_Day": "Previous Day",
"Inherit": "Inherit", "Inherit": "Inherit",
"IgnoreInherit": "Do Not Inherit Fields", "InheritFields": "Inherit Fields Values",
"FoodInherit": "Food Inheritable Fields", "FoodInherit": "Food Inheritable Fields",
"ShowUncategorizedFood": "Show Undefined", "ShowUncategorizedFood": "Show Undefined",
"GroupBy": "Group By", "GroupBy": "Group By",

View File

@ -76,7 +76,7 @@ export class Models {
// REQUIRED: unordered array of fields that can be set during create // REQUIRED: unordered array of fields that can be set during create
create: { create: {
// if not defined partialUpdate will use the same parameters, prepending 'id' // if not defined partialUpdate will use the same parameters, prepending 'id'
params: [["name", "description", "recipe", "food_onhand", "supermarket_category", "inherit", "ignore_inherit"]], params: [["name", "description", "recipe", "food_onhand", "supermarket_category", "inherit", "inherit_fields"]],
form: { form: {
name: { name: {
@ -114,19 +114,14 @@ export class Models {
label: i18n.t("Shopping_Category"), label: i18n.t("Shopping_Category"),
allow_create: true, allow_create: true,
}, },
inherit: { inherit_fields: {
form_field: true,
type: "checkbox",
field: "inherit",
label: i18n.t("Inherit"),
},
ignore_inherit: {
form_field: true, form_field: true,
type: "lookup", type: "lookup",
multiple: true, multiple: true,
field: "ignore_inherit", field: "inherit_fields",
list: "FOOD_INHERIT_FIELDS", list: "FOOD_INHERIT_FIELDS",
label: i18n.t("IgnoreInherit"), label: i18n.t("InheritFields"),
condition: { field: "parent", value: true, condition: "exists" },
}, },
full_name: { full_name: {
form_field: true, form_field: true,

View File

@ -214,7 +214,7 @@ export interface Food {
* @type {boolean} * @type {boolean}
* @memberof Food * @memberof Food
*/ */
ignore_shopping?: boolean; food_onhand?: boolean;
/** /**
* *
* @type {FoodSupermarketCategory} * @type {FoodSupermarketCategory}
@ -235,47 +235,16 @@ export interface Food {
numchild?: number; numchild?: number;
/** /**
* *
* @type {boolean} * @type {Array<FoodInheritFields>}
* @memberof Food * @memberof Food
*/ */
on_hand?: boolean; inherit_fields?: Array<FoodInheritFields> | null;
/**
*
* @type {boolean}
* @memberof Food
*/
inherit?: boolean;
/**
*
* @type {Array<FoodIgnoreInherit>}
* @memberof Food
*/
ignore_inherit?: Array<FoodIgnoreInherit> | null;
}
/**
*
* @export
* @interface FoodIgnoreInherit
*/
export interface FoodIgnoreInherit {
/**
*
* @type {number}
* @memberof FoodIgnoreInherit
*/
id?: number;
/** /**
* *
* @type {string} * @type {string}
* @memberof FoodIgnoreInherit * @memberof Food
*/ */
name?: string; full_name?: string;
/**
*
* @type {string}
* @memberof FoodIgnoreInherit
*/
field?: string;
} }
/** /**
* *
@ -294,13 +263,38 @@ export interface FoodInheritField {
* @type {string} * @type {string}
* @memberof FoodInheritField * @memberof FoodInheritField
*/ */
name?: string; name?: string | null;
/** /**
* *
* @type {string} * @type {string}
* @memberof FoodInheritField * @memberof FoodInheritField
*/ */
field?: string; field?: string | null;
}
/**
*
* @export
* @interface FoodInheritFields
*/
export interface FoodInheritFields {
/**
*
* @type {number}
* @memberof FoodInheritFields
*/
id?: number;
/**
*
* @type {string}
* @memberof FoodInheritFields
*/
name?: string | null;
/**
*
* @type {string}
* @memberof FoodInheritFields
*/
field?: string | null;
} }
/** /**
* *
@ -513,6 +507,12 @@ export interface ImportLogKeyword {
* @memberof ImportLogKeyword * @memberof ImportLogKeyword
*/ */
updated_at?: string; updated_at?: string;
/**
*
* @type {string}
* @memberof ImportLogKeyword
*/
full_name?: string;
} }
/** /**
* *
@ -610,7 +610,7 @@ export interface IngredientFood {
* @type {boolean} * @type {boolean}
* @memberof IngredientFood * @memberof IngredientFood
*/ */
ignore_shopping?: boolean; food_onhand?: boolean;
/** /**
* *
* @type {FoodSupermarketCategory} * @type {FoodSupermarketCategory}
@ -631,22 +631,16 @@ export interface IngredientFood {
numchild?: number; numchild?: number;
/** /**
* *
* @type {boolean} * @type {Array<FoodInheritFields>}
* @memberof IngredientFood * @memberof IngredientFood
*/ */
on_hand?: boolean; inherit_fields?: Array<FoodInheritFields> | null;
/** /**
* *
* @type {boolean} * @type {string}
* @memberof IngredientFood * @memberof IngredientFood
*/ */
inherit?: boolean; full_name?: string;
/**
*
* @type {Array<FoodIgnoreInherit>}
* @memberof IngredientFood
*/
ignore_inherit?: Array<FoodIgnoreInherit> | null;
} }
/** /**
* *
@ -1018,6 +1012,12 @@ export interface Keyword {
* @memberof Keyword * @memberof Keyword
*/ */
updated_at?: string; updated_at?: string;
/**
*
* @type {string}
* @memberof Keyword
*/
full_name?: string;
} }
/** /**
* *
@ -1691,6 +1691,12 @@ export interface RecipeKeywords {
* @memberof RecipeKeywords * @memberof RecipeKeywords
*/ */
updated_at?: string; updated_at?: string;
/**
*
* @type {string}
* @memberof RecipeKeywords
*/
full_name?: string;
} }
/** /**
* *
@ -2996,10 +3002,10 @@ export interface UserPreference {
mealplan_autoadd_shopping?: boolean; mealplan_autoadd_shopping?: boolean;
/** /**
* *
* @type {string} * @type {Array<FoodInheritFields>}
* @memberof UserPreference * @memberof UserPreference
*/ */
food_ignore_default?: string; food_inherit_default?: Array<FoodInheritFields> | null;
/** /**
* *
* @type {string} * @type {string}
@ -3042,6 +3048,12 @@ export interface UserPreference {
* @memberof UserPreference * @memberof UserPreference
*/ */
csv_prefix?: string; csv_prefix?: string;
/**
*
* @type {boolean}
* @memberof UserPreference
*/
filter_to_supermarket?: boolean;
} }
/** /**

View File

@ -220,11 +220,6 @@ export const ApiMixin = {
return { return {
Models: Models, Models: Models,
Actions: Actions, Actions: Actions,
FoodCreateDefault: function (form) {
form.inherit_ignore = getUserPreference("food_ignore_default")
form.inherit = form.supermarket_category.length > 0
return form
},
} }
}, },
methods: { methods: {
@ -369,6 +364,7 @@ export function getForm(model, action, item1, item2) {
if (f === "partialUpdate" && Object.keys(config).length == 0) { if (f === "partialUpdate" && Object.keys(config).length == 0) {
config = { ...Actions.CREATE?.form, ...model.model_type?.["create"]?.form, ...model?.["create"]?.form } config = { ...Actions.CREATE?.form, ...model.model_type?.["create"]?.form, ...model?.["create"]?.form }
config["title"] = { ...action?.form_title, ...model.model_type?.[f]?.form_title, ...model?.[f]?.form_title } config["title"] = { ...action?.form_title, ...model.model_type?.[f]?.form_title, ...model?.[f]?.form_title }
// form functions should not be inherited
if (config?.["form_function"]?.includes("Create")) { if (config?.["form_function"]?.includes("Create")) {
delete config["form_function"] delete config["form_function"]
} }
@ -542,8 +538,7 @@ const specialCases = {
export const formFunctions = { export const formFunctions = {
FoodCreateDefault: function (form) { FoodCreateDefault: function (form) {
form.fields.filter((x) => x.field === "ignore_inherit")[0].value = getUserPreference("food_ignore_default") form.fields.filter((x) => x.field === "inherit_fields")[0].value = getUserPreference("food_inherit_default")
form.fields.filter((x) => x.field === "inherit")[0].value = getUserPreference("food_ignore_default").length > 0
return form return form
}, },
} }