diff --git a/alembic/versions/2023-09-01-14.55.42_0341b154f79a_added_normalized_unit_and_food_names.py b/alembic/versions/2023-09-01-14.55.42_0341b154f79a_added_normalized_unit_and_food_names.py index f7b399e1dbc..44efe74faf8 100644 --- a/alembic/versions/2023-09-01-14.55.42_0341b154f79a_added_normalized_unit_and_food_names.py +++ b/alembic/versions/2023-09-01-14.55.42_0341b154f79a_added_normalized_unit_and_food_names.py @@ -22,7 +22,15 @@ def populate_normalized_fields(): bind = op.get_bind() session = orm.Session(bind=bind) - units = session.execute(select(IngredientUnitModel)).scalars().all() + units = ( + session.execute( + select(IngredientUnitModel).options( + orm.load_only(IngredientUnitModel.name, IngredientUnitModel.abbreviation) + ) + ) + .scalars() + .all() + ) for unit in units: if unit.name is not None: unit.name_normalized = IngredientUnitModel.normalize(unit.name) @@ -32,7 +40,9 @@ def populate_normalized_fields(): session.add(unit) - foods = session.execute(select(IngredientFoodModel)).scalars().all() + foods = ( + session.execute(select(IngredientFoodModel).options(orm.load_only(IngredientFoodModel.name))).scalars().all() + ) for food in foods: if food.name is not None: food.name_normalized = IngredientFoodModel.normalize(food.name) diff --git a/alembic/versions/2023-10-04-14.29.26_dded3119c1fe_added_unique_constraints.py b/alembic/versions/2023-10-04-14.29.26_dded3119c1fe_added_unique_constraints.py index 8074b906ca1..fe624cf20da 100644 --- a/alembic/versions/2023-10-04-14.29.26_dded3119c1fe_added_unique_constraints.py +++ b/alembic/versions/2023-10-04-14.29.26_dded3119c1fe_added_unique_constraints.py @@ -10,7 +10,7 @@ from typing import Any import sqlalchemy as sa -from sqlalchemy.orm import Session +from sqlalchemy.orm import Session, load_only import mealie.db.migration_types from alembic import op @@ -44,7 +44,7 @@ def _is_postgres(): def _get_duplicates(session: Session, model: SqlAlchemyBase) -> defaultdict[str, list[str]]: duplicate_map: defaultdict[str, list[str]] = defaultdict(list) - for obj in session.query(model).all(): + for obj in session.query(model).options(load_only(model.id, model.group_id, model.name)).all(): key = f"{obj.group_id}$${obj.name}" duplicate_map[key].append(str(obj.id)) @@ -117,9 +117,9 @@ def _resolve_duplivate_foods_units_labels(): continue keep_id = ids[0] - keep_obj = session.query(model).filter_by(id=keep_id).first() + keep_obj = session.query(model).options(load_only(model.id)).filter_by(id=keep_id).first() for dupe_id in ids[1:]: - dupe_obj = session.query(model).filter_by(id=dupe_id).first() + dupe_obj = session.query(model).options(load_only(model.id)).filter_by(id=dupe_id).first() resolve_func(session, keep_obj, dupe_obj) diff --git a/alembic/versions/2023-10-19-19.22.55_ba1e4a6cfe99_added_plural_names_and_alias_tables_for_.py b/alembic/versions/2023-10-19-19.22.55_ba1e4a6cfe99_added_plural_names_and_alias_tables_for_.py new file mode 100644 index 00000000000..23952dc704a --- /dev/null +++ b/alembic/versions/2023-10-19-19.22.55_ba1e4a6cfe99_added_plural_names_and_alias_tables_for_.py @@ -0,0 +1,106 @@ +"""added plural names and alias tables for foods and units + +Revision ID: ba1e4a6cfe99 +Revises: dded3119c1fe +Create Date: 2023-10-19 19:22:55.369319 + +""" +import sqlalchemy as sa + +import mealie.db.migration_types +from alembic import op + +# revision identifiers, used by Alembic. +revision = "ba1e4a6cfe99" +down_revision = "dded3119c1fe" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "ingredient_units_aliases", + sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), + sa.Column("unit_id", mealie.db.migration_types.GUID(), nullable=False), + sa.Column("name", sa.String(), nullable=False), + sa.Column("name_normalized", sa.String(), nullable=True), + sa.Column("created_at", sa.DateTime(), nullable=True), + sa.Column("update_at", sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint( + ["unit_id"], + ["ingredient_units.id"], + ), + sa.PrimaryKeyConstraint("id", "unit_id"), + ) + op.create_index( + op.f("ix_ingredient_units_aliases_created_at"), "ingredient_units_aliases", ["created_at"], unique=False + ) + op.create_index( + op.f("ix_ingredient_units_aliases_name_normalized"), + "ingredient_units_aliases", + ["name_normalized"], + unique=False, + ) + op.create_table( + "ingredient_foods_aliases", + sa.Column("id", mealie.db.migration_types.GUID(), nullable=False), + sa.Column("food_id", mealie.db.migration_types.GUID(), nullable=False), + sa.Column("name", sa.String(), nullable=False), + sa.Column("name_normalized", sa.String(), nullable=True), + sa.Column("created_at", sa.DateTime(), nullable=True), + sa.Column("update_at", sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint( + ["food_id"], + ["ingredient_foods.id"], + ), + sa.PrimaryKeyConstraint("id", "food_id"), + ) + op.create_index( + op.f("ix_ingredient_foods_aliases_created_at"), "ingredient_foods_aliases", ["created_at"], unique=False + ) + op.create_index( + op.f("ix_ingredient_foods_aliases_name_normalized"), + "ingredient_foods_aliases", + ["name_normalized"], + unique=False, + ) + op.add_column("ingredient_foods", sa.Column("plural_name", sa.String(), nullable=True)) + op.add_column("ingredient_foods", sa.Column("plural_name_normalized", sa.String(), nullable=True)) + op.create_index( + op.f("ix_ingredient_foods_plural_name_normalized"), "ingredient_foods", ["plural_name_normalized"], unique=False + ) + op.add_column("ingredient_units", sa.Column("plural_name", sa.String(), nullable=True)) + op.add_column("ingredient_units", sa.Column("plural_name_normalized", sa.String(), nullable=True)) + op.create_index( + op.f("ix_ingredient_units_plural_name_normalized"), "ingredient_units", ["plural_name_normalized"], unique=False + ) + op.add_column("ingredient_units", sa.Column("plural_abbreviation", sa.String(), nullable=True)) + op.add_column("ingredient_units", sa.Column("plural_abbreviation_normalized", sa.String(), nullable=True)) + op.create_index( + op.f("ix_ingredient_units_plural_abbreviation_normalized"), + "ingredient_units", + ["plural_abbreviation_normalized"], + unique=False, + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index(op.f("ix_ingredient_units_plural_abbreviation_normalized"), table_name="ingredient_units") + op.drop_column("ingredient_units", "plural_abbreviation_normalized") + op.drop_column("ingredient_units", "plural_abbreviation") + op.drop_index(op.f("ix_ingredient_units_plural_name_normalized"), table_name="ingredient_units") + op.drop_column("ingredient_units", "plural_name_normalized") + op.drop_column("ingredient_units", "plural_name") + op.drop_index(op.f("ix_ingredient_foods_plural_name_normalized"), table_name="ingredient_foods") + op.drop_column("ingredient_foods", "plural_name_normalized") + op.drop_column("ingredient_foods", "plural_name") + op.drop_index(op.f("ix_ingredient_foods_aliases_name_normalized"), table_name="ingredient_foods_aliases") + op.drop_index(op.f("ix_ingredient_foods_aliases_created_at"), table_name="ingredient_foods_aliases") + op.drop_table("ingredient_foods_aliases") + op.drop_index(op.f("ix_ingredient_units_aliases_name_normalized"), table_name="ingredient_units_aliases") + op.drop_index(op.f("ix_ingredient_units_aliases_created_at"), table_name="ingredient_units_aliases") + op.drop_table("ingredient_units_aliases") + # ### end Alembic commands ### diff --git a/frontend/components/Domain/Recipe/RecipeDataAliasManagerDialog.vue b/frontend/components/Domain/Recipe/RecipeDataAliasManagerDialog.vue new file mode 100644 index 00000000000..6b0741f53c4 --- /dev/null +++ b/frontend/components/Domain/Recipe/RecipeDataAliasManagerDialog.vue @@ -0,0 +1,141 @@ + + + diff --git a/frontend/components/global/BaseDialog.vue b/frontend/components/global/BaseDialog.vue index a7f17618975..30a0c9aaf38 100644 --- a/frontend/components/global/BaseDialog.vue +++ b/frontend/components/global/BaseDialog.vue @@ -58,8 +58,12 @@ {{ $t("general.confirm") }} + {{ submitText }} + @@ -109,6 +113,10 @@ export default defineComponent({ default: null, type: Boolean, }, + submitIcon: { + type: String, + default: null, + }, submitText: { type: String, default: function () { diff --git a/frontend/composables/recipes/use-recipe-ingredients.test.ts b/frontend/composables/recipes/use-recipe-ingredients.test.ts index c7e6900bea7..7850dca1d87 100644 --- a/frontend/composables/recipes/use-recipe-ingredients.test.ts +++ b/frontend/composables/recipes/use-recipe-ingredients.test.ts @@ -48,4 +48,74 @@ describe(parseIngredientText.name, () => { expect(parseIngredientText(ingredient, false)).not.toContain("