mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-03-12 09:25:07 -04:00
feat: Move alembic config into mealie package for easier distribution (#4329)
This commit is contained in:
1
mealie/alembic/README
Normal file
1
mealie/alembic/README
Normal file
@@ -0,0 +1 @@
|
||||
Generic single-database configuration.
|
||||
60
mealie/alembic/alembic.ini
Normal file
60
mealie/alembic/alembic.ini
Normal file
@@ -0,0 +1,60 @@
|
||||
# A generic, single database configuration.
|
||||
|
||||
[alembic]
|
||||
# path to migration scripts
|
||||
script_location = %(here)s
|
||||
|
||||
# template used to generate migration files
|
||||
file_template = %%(year)d-%%(month).2d-%%(day).2d-%%(hour).2d.%%(minute).2d.%%(second).2d_%%(rev)s_%%(slug)s
|
||||
|
||||
# sys.path path, will be prepended to sys.path if present.
|
||||
# defaults to the current working directory.
|
||||
prepend_sys_path = .
|
||||
|
||||
# timezone to use when rendering the date within the migration file
|
||||
# as well as the filename.
|
||||
# If specified, requires the python-dateutil library that can be
|
||||
# installed by adding `alembic[tz]` to the pip requirements
|
||||
# string value is passed to dateutil.tz.gettz()
|
||||
# leave blank for localtime
|
||||
# timezone =
|
||||
|
||||
# max length of characters to apply to the
|
||||
# "slug" field
|
||||
# truncate_slug_length = 40
|
||||
|
||||
# set to 'true' to run the environment during
|
||||
# the 'revision' command, regardless of autogenerate
|
||||
# revision_environment = false
|
||||
|
||||
# set to 'true' to allow .pyc and .pyo files without
|
||||
# a source .py file to be detected as revisions in the
|
||||
# versions/ directory
|
||||
# sourceless = false
|
||||
|
||||
# version location specification; This defaults
|
||||
# to alembic/versions. When using multiple version
|
||||
# directories, initial revisions must be specified with --version-path.
|
||||
# The path separator used here should be the separator specified by "version_path_separator"
|
||||
# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions
|
||||
|
||||
# version path separator; As mentioned above, this is the character used to split
|
||||
# version_locations. Valid values are:
|
||||
#
|
||||
# version_path_separator = :
|
||||
# version_path_separator = ;
|
||||
# version_path_separator = space
|
||||
version_path_separator = os # default: use os.pathsep
|
||||
|
||||
# the output encoding used when revision files
|
||||
# are written from script.py.mako
|
||||
# output_encoding = utf-8
|
||||
|
||||
# Will be replaced in alembic/env.py with the actual url
|
||||
sqlalchemy.url =
|
||||
|
||||
|
||||
[post_write_hooks]
|
||||
# post_write_hooks defines scripts or Python functions that are run
|
||||
# on newly generated revision scripts. See the documentation for further
|
||||
# detail and examples
|
||||
109
mealie/alembic/env.py
Normal file
109
mealie/alembic/env.py
Normal file
@@ -0,0 +1,109 @@
|
||||
from typing import Any
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import context
|
||||
|
||||
import mealie.db.models._all_models # noqa: F401
|
||||
from mealie.core.config import get_app_settings
|
||||
from mealie.db.models._model_base import SqlAlchemyBase
|
||||
|
||||
# this is the Alembic Config object, which provides
|
||||
# access to the values within the .ini file in use.
|
||||
config = context.config
|
||||
|
||||
# add your model's MetaData object here
|
||||
# for 'autogenerate' support
|
||||
# from myapp import mymodel
|
||||
# target_metadata = mymodel.Base.metadata
|
||||
target_metadata = SqlAlchemyBase.metadata
|
||||
|
||||
# other values from the config, defined by the needs of env.py,
|
||||
# can be acquired:
|
||||
# my_important_option = config.get_main_option("my_important_option")
|
||||
# ... etc.
|
||||
|
||||
# Set DB url from config
|
||||
settings = get_app_settings()
|
||||
|
||||
if not settings.DB_URL:
|
||||
raise Exception("DB URL not set in config")
|
||||
|
||||
config.set_main_option("sqlalchemy.url", settings.DB_URL.replace("%", "%%"))
|
||||
|
||||
|
||||
def include_object(object: Any, name: str, type_: str, reflected: bool, compare_to: Any):
|
||||
# skip dropping food/unit unique constraints; they are defined manually so alembic doesn't see them
|
||||
# see: revision dded3119c1fe
|
||||
if type_ == "unique_constraint" and name == "ingredient_foods_name_group_id_key" and compare_to is None:
|
||||
return False
|
||||
if type_ == "unique_constraint" and name == "ingredient_units_name_group_id_key" and compare_to is None:
|
||||
return False
|
||||
|
||||
# skip changing the quantity column in recipes_ingredients; it's a float on postgres, but an integer on sqlite
|
||||
# see: revision 263dd6707191
|
||||
if (
|
||||
type_ == "column"
|
||||
and name == "quantity"
|
||||
and object.table.name == "recipes_ingredients"
|
||||
and hasattr(compare_to, "type")
|
||||
and isinstance(compare_to.type, sa.Integer)
|
||||
):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def run_migrations_offline():
|
||||
"""Run migrations in 'offline' mode.
|
||||
|
||||
This configures the context with just a URL
|
||||
and not an Engine, though an Engine is acceptable
|
||||
here as well. By skipping the Engine creation
|
||||
we don't even need a DBAPI to be available.
|
||||
|
||||
Calls to context.execute() here emit the given string to the
|
||||
script output.
|
||||
|
||||
"""
|
||||
url = config.get_main_option("sqlalchemy.url")
|
||||
context.configure(
|
||||
url=url,
|
||||
target_metadata=target_metadata,
|
||||
literal_binds=True,
|
||||
dialect_opts={"paramstyle": "named"},
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
def run_migrations_online():
|
||||
"""Run migrations in 'online' mode.
|
||||
|
||||
In this scenario we need to create an Engine
|
||||
and associate a connection with the context.
|
||||
|
||||
"""
|
||||
connectable = sa.engine_from_config(
|
||||
config.get_section(config.config_ini_section),
|
||||
prefix="sqlalchemy.",
|
||||
poolclass=sa.pool.NullPool,
|
||||
)
|
||||
|
||||
with connectable.connect() as connection:
|
||||
context.configure(
|
||||
connection=connection,
|
||||
target_metadata=target_metadata,
|
||||
user_module_prefix="mealie.db.migration_types.",
|
||||
render_as_batch=True,
|
||||
include_object=include_object,
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
if context.is_offline_mode():
|
||||
run_migrations_offline()
|
||||
else:
|
||||
run_migrations_online()
|
||||
28
mealie/alembic/script.py.mako
Normal file
28
mealie/alembic/script.py.mako
Normal file
@@ -0,0 +1,28 @@
|
||||
"""${message}
|
||||
|
||||
Revision ID: ${up_revision}
|
||||
Revises: ${down_revision | comma,n}
|
||||
Create Date: ${create_date}
|
||||
|
||||
"""
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
import mealie.db.migration_types
|
||||
% if imports:
|
||||
${imports}
|
||||
% endif
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = ${repr(up_revision)}
|
||||
down_revision: str | None = ${repr(down_revision)}
|
||||
branch_labels: str | tuple[str, ...] | None = ${repr(branch_labels)}
|
||||
depends_on: str | tuple[str, ...] | None = ${repr(depends_on)}
|
||||
|
||||
|
||||
def upgrade():
|
||||
${upgrades if upgrades else "pass"}
|
||||
|
||||
|
||||
def downgrade():
|
||||
${downgrades if downgrades else "pass"}
|
||||
@@ -0,0 +1,921 @@
|
||||
"""Initial tables
|
||||
|
||||
Revision ID: 6b0f5f32d602
|
||||
Revises:
|
||||
Create Date: 2022-02-21 19:56:24.351115
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import engine_from_config
|
||||
|
||||
import mealie.db.migration_types
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "6b0f5f32d602"
|
||||
down_revision: str | None = None
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
# Adapted from https://improveandrepeat.com/2021/09/python-friday-87-handling-pre-existing-tables-with-alembic-and-sqlalchemy/
|
||||
def table_exists(table, schema=None):
|
||||
config = op.get_context().config
|
||||
engine = engine_from_config(config.get_section(config.config_ini_section), prefix="sqlalchemy.")
|
||||
insp = sa.inspect(engine)
|
||||
return insp.has_table(table, schema)
|
||||
|
||||
|
||||
def upgrade():
|
||||
# Only create initial tables if they don't exist yet, to ease transition from pre-alembic state
|
||||
if table_exists("users"):
|
||||
return
|
||||
|
||||
op.create_table(
|
||||
"groups",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("name", sa.String(), nullable=False),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_groups_name"), "groups", ["name"], unique=True)
|
||||
op.create_table(
|
||||
"categories",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("name", sa.String(), nullable=False),
|
||||
sa.Column("slug", sa.String(), nullable=False),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_id"],
|
||||
["groups.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("slug", "group_id", name="category_slug_group_id_key"),
|
||||
)
|
||||
op.create_index(op.f("ix_categories_group_id"), "categories", ["group_id"], unique=False)
|
||||
op.create_index(op.f("ix_categories_name"), "categories", ["name"], unique=False)
|
||||
op.create_index(op.f("ix_categories_slug"), "categories", ["slug"], unique=False)
|
||||
op.create_table(
|
||||
"cookbooks",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("position", sa.Integer(), nullable=False),
|
||||
sa.Column("name", sa.String(), nullable=False),
|
||||
sa.Column("slug", sa.String(), nullable=False),
|
||||
sa.Column("description", sa.String(), nullable=True),
|
||||
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_id"],
|
||||
["groups.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_table(
|
||||
"group_data_exports",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("name", sa.String(), nullable=False),
|
||||
sa.Column("filename", sa.String(), nullable=False),
|
||||
sa.Column("path", sa.String(), nullable=False),
|
||||
sa.Column("size", sa.String(), nullable=False),
|
||||
sa.Column("expires", sa.String(), nullable=False),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_id"],
|
||||
["groups.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_group_data_exports_group_id"), "group_data_exports", ["group_id"], unique=False)
|
||||
op.create_table(
|
||||
"group_events_notifiers",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("name", sa.String(), nullable=False),
|
||||
sa.Column("enabled", sa.Boolean(), nullable=False),
|
||||
sa.Column("apprise_url", sa.String(), nullable=False),
|
||||
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_id"],
|
||||
["groups.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_group_events_notifiers_group_id"), "group_events_notifiers", ["group_id"], unique=False)
|
||||
op.create_table(
|
||||
"group_meal_plan_rules",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("day", sa.String(), nullable=False),
|
||||
sa.Column("entry_type", sa.String(), nullable=False),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_id"],
|
||||
["groups.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_table(
|
||||
"group_preferences",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("private_group", sa.Boolean(), nullable=True),
|
||||
sa.Column("first_day_of_week", sa.Integer(), nullable=True),
|
||||
sa.Column("recipe_public", sa.Boolean(), nullable=True),
|
||||
sa.Column("recipe_show_nutrition", sa.Boolean(), nullable=True),
|
||||
sa.Column("recipe_show_assets", sa.Boolean(), nullable=True),
|
||||
sa.Column("recipe_landscape_view", sa.Boolean(), nullable=True),
|
||||
sa.Column("recipe_disable_comments", sa.Boolean(), nullable=True),
|
||||
sa.Column("recipe_disable_amount", sa.Boolean(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_id"],
|
||||
["groups.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_group_preferences_group_id"), "group_preferences", ["group_id"], unique=False)
|
||||
op.create_table(
|
||||
"group_reports",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("name", sa.String(), nullable=False),
|
||||
sa.Column("status", sa.String(), nullable=False),
|
||||
sa.Column("category", sa.String(), nullable=False),
|
||||
sa.Column("timestamp", sa.DateTime(), nullable=False),
|
||||
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_id"],
|
||||
["groups.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_group_reports_category"), "group_reports", ["category"], unique=False)
|
||||
op.create_index(op.f("ix_group_reports_group_id"), "group_reports", ["group_id"], unique=False)
|
||||
op.create_table(
|
||||
"ingredient_units",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("name", sa.String(), nullable=True),
|
||||
sa.Column("description", sa.String(), nullable=True),
|
||||
sa.Column("abbreviation", sa.String(), nullable=True),
|
||||
sa.Column("fraction", sa.Boolean(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_id"],
|
||||
["groups.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_table(
|
||||
"invite_tokens",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("token", sa.String(), nullable=False),
|
||||
sa.Column("uses_left", sa.Integer(), nullable=False),
|
||||
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_id"],
|
||||
["groups.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_invite_tokens_token"), "invite_tokens", ["token"], unique=True)
|
||||
op.create_table(
|
||||
"multi_purpose_labels",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("name", sa.String(length=255), nullable=False),
|
||||
sa.Column("color", sa.String(length=10), nullable=False),
|
||||
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_id"],
|
||||
["groups.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_multi_purpose_labels_group_id"), "multi_purpose_labels", ["group_id"], unique=False)
|
||||
op.create_table(
|
||||
"recipes",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("slug", sa.String(), nullable=True),
|
||||
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("user_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("name", sa.String(), nullable=False),
|
||||
sa.Column("description", sa.String(), nullable=True),
|
||||
sa.Column("image", sa.String(), nullable=True),
|
||||
sa.Column("total_time", sa.String(), nullable=True),
|
||||
sa.Column("prep_time", sa.String(), nullable=True),
|
||||
sa.Column("perform_time", sa.String(), nullable=True),
|
||||
sa.Column("cook_time", sa.String(), nullable=True),
|
||||
sa.Column("recipe_yield", sa.String(), nullable=True),
|
||||
sa.Column("recipeCuisine", sa.String(), nullable=True),
|
||||
sa.Column("rating", sa.Integer(), nullable=True),
|
||||
sa.Column("org_url", sa.String(), nullable=True),
|
||||
sa.Column("date_added", sa.Date(), nullable=True),
|
||||
sa.Column("date_updated", sa.DateTime(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_id"],
|
||||
["groups.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(["user_id"], ["users.id"], use_alter=True),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("slug", "group_id", name="recipe_slug_group_id_key"),
|
||||
)
|
||||
op.create_index(op.f("ix_recipes_group_id"), "recipes", ["group_id"], unique=False)
|
||||
op.create_index(op.f("ix_recipes_slug"), "recipes", ["slug"], unique=False)
|
||||
op.create_index(op.f("ix_recipes_user_id"), "recipes", ["user_id"], unique=False)
|
||||
op.create_table(
|
||||
"server_tasks",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("name", sa.String(), nullable=False),
|
||||
sa.Column("completed_date", sa.DateTime(), nullable=True),
|
||||
sa.Column("status", sa.String(), nullable=False),
|
||||
sa.Column("log", sa.String(), nullable=True),
|
||||
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_id"],
|
||||
["groups.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_server_tasks_group_id"), "server_tasks", ["group_id"], unique=False)
|
||||
op.create_table(
|
||||
"shopping_lists",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("name", sa.String(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_id"],
|
||||
["groups.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_shopping_lists_group_id"), "shopping_lists", ["group_id"], unique=False)
|
||||
op.create_table(
|
||||
"tags",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("name", sa.String(), nullable=False),
|
||||
sa.Column("slug", sa.String(), nullable=False),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_id"],
|
||||
["groups.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("slug", "group_id", name="tags_slug_group_id_key"),
|
||||
)
|
||||
op.create_index(op.f("ix_tags_group_id"), "tags", ["group_id"], unique=False)
|
||||
op.create_index(op.f("ix_tags_name"), "tags", ["name"], unique=False)
|
||||
op.create_index(op.f("ix_tags_slug"), "tags", ["slug"], unique=False)
|
||||
op.create_table(
|
||||
"tools",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("name", sa.String(), nullable=False),
|
||||
sa.Column("slug", sa.String(), nullable=False),
|
||||
sa.Column("on_hand", sa.Boolean(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_id"],
|
||||
["groups.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("slug", "group_id", name="tools_slug_group_id_key"),
|
||||
)
|
||||
op.create_index(op.f("ix_tools_name"), "tools", ["name"], unique=True)
|
||||
op.create_index(op.f("ix_tools_slug"), "tools", ["slug"], unique=True)
|
||||
op.create_table(
|
||||
"webhook_urls",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("enabled", sa.Boolean(), nullable=True),
|
||||
sa.Column("name", sa.String(), nullable=True),
|
||||
sa.Column("url", sa.String(), nullable=True),
|
||||
sa.Column("time", sa.String(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_id"],
|
||||
["groups.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_webhook_urls_group_id"), "webhook_urls", ["group_id"], unique=False)
|
||||
op.create_table(
|
||||
"api_extras",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("recipee_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("key_name", sa.String(), nullable=True),
|
||||
sa.Column("value", sa.String(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["recipee_id"],
|
||||
["recipes.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_table(
|
||||
"cookbooks_to_categories",
|
||||
sa.Column("cookbook_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("category_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["category_id"],
|
||||
["categories.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["cookbook_id"],
|
||||
["cookbooks.id"],
|
||||
),
|
||||
)
|
||||
op.create_table(
|
||||
"group_events_notifier_options",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("event_notifier_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("recipe_created", sa.Boolean(), nullable=False),
|
||||
sa.Column("recipe_updated", sa.Boolean(), nullable=False),
|
||||
sa.Column("recipe_deleted", sa.Boolean(), nullable=False),
|
||||
sa.Column("user_signup", sa.Boolean(), nullable=False),
|
||||
sa.Column("data_migrations", sa.Boolean(), nullable=False),
|
||||
sa.Column("data_export", sa.Boolean(), nullable=False),
|
||||
sa.Column("data_import", sa.Boolean(), nullable=False),
|
||||
sa.Column("mealplan_entry_created", sa.Boolean(), nullable=False),
|
||||
sa.Column("shopping_list_created", sa.Boolean(), nullable=False),
|
||||
sa.Column("shopping_list_updated", sa.Boolean(), nullable=False),
|
||||
sa.Column("shopping_list_deleted", sa.Boolean(), nullable=False),
|
||||
sa.Column("cookbook_created", sa.Boolean(), nullable=False),
|
||||
sa.Column("cookbook_updated", sa.Boolean(), nullable=False),
|
||||
sa.Column("cookbook_deleted", sa.Boolean(), nullable=False),
|
||||
sa.Column("tag_created", sa.Boolean(), nullable=False),
|
||||
sa.Column("tag_updated", sa.Boolean(), nullable=False),
|
||||
sa.Column("tag_deleted", sa.Boolean(), nullable=False),
|
||||
sa.Column("category_created", sa.Boolean(), nullable=False),
|
||||
sa.Column("category_updated", sa.Boolean(), nullable=False),
|
||||
sa.Column("category_deleted", sa.Boolean(), nullable=False),
|
||||
sa.ForeignKeyConstraint(
|
||||
["event_notifier_id"],
|
||||
["group_events_notifiers.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_table(
|
||||
"group_meal_plans",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("date", sa.Date(), nullable=False),
|
||||
sa.Column("entry_type", sa.String(), nullable=False),
|
||||
sa.Column("title", sa.String(), nullable=False),
|
||||
sa.Column("text", sa.String(), nullable=False),
|
||||
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_id"],
|
||||
["groups.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["recipe_id"],
|
||||
["recipes.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_group_meal_plans_date"), "group_meal_plans", ["date"], unique=False)
|
||||
op.create_index(op.f("ix_group_meal_plans_entry_type"), "group_meal_plans", ["entry_type"], unique=False)
|
||||
op.create_index(op.f("ix_group_meal_plans_group_id"), "group_meal_plans", ["group_id"], unique=False)
|
||||
op.create_index(op.f("ix_group_meal_plans_recipe_id"), "group_meal_plans", ["recipe_id"], unique=False)
|
||||
op.create_index(op.f("ix_group_meal_plans_title"), "group_meal_plans", ["title"], unique=False)
|
||||
op.create_table(
|
||||
"group_to_categories",
|
||||
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("category_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["category_id"],
|
||||
["categories.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_id"],
|
||||
["groups.id"],
|
||||
),
|
||||
)
|
||||
op.create_table(
|
||||
"ingredient_foods",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("name", sa.String(), nullable=True),
|
||||
sa.Column("description", sa.String(), nullable=True),
|
||||
sa.Column("label_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_id"],
|
||||
["groups.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["label_id"],
|
||||
["multi_purpose_labels.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_table(
|
||||
"notes",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("title", sa.String(), nullable=True),
|
||||
sa.Column("text", sa.String(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["recipe_id"],
|
||||
["recipes.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_table(
|
||||
"plan_rules_to_categories",
|
||||
sa.Column("group_plan_rule_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("category_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["category_id"],
|
||||
["categories.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_plan_rule_id"],
|
||||
["group_meal_plan_rules.id"],
|
||||
),
|
||||
)
|
||||
op.create_table(
|
||||
"plan_rules_to_tags",
|
||||
sa.Column("plan_rule_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("tag_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["plan_rule_id"],
|
||||
["group_meal_plan_rules.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["tag_id"],
|
||||
["tags.id"],
|
||||
),
|
||||
)
|
||||
op.create_table(
|
||||
"recipe_assets",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("name", sa.String(), nullable=True),
|
||||
sa.Column("icon", sa.String(), nullable=True),
|
||||
sa.Column("file_name", sa.String(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["recipe_id"],
|
||||
["recipes.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_table(
|
||||
"recipe_instructions",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("position", sa.Integer(), nullable=True),
|
||||
sa.Column("type", sa.String(), nullable=True),
|
||||
sa.Column("title", sa.String(), nullable=True),
|
||||
sa.Column("text", sa.String(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["recipe_id"],
|
||||
["recipes.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_table(
|
||||
"recipe_nutrition",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("calories", sa.String(), nullable=True),
|
||||
sa.Column("fat_content", sa.String(), nullable=True),
|
||||
sa.Column("fiber_content", sa.String(), nullable=True),
|
||||
sa.Column("protein_content", sa.String(), nullable=True),
|
||||
sa.Column("carbohydrate_content", sa.String(), nullable=True),
|
||||
sa.Column("sodium_content", sa.String(), nullable=True),
|
||||
sa.Column("sugar_content", sa.String(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["recipe_id"],
|
||||
["recipes.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_table(
|
||||
"recipe_settings",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("public", sa.Boolean(), nullable=True),
|
||||
sa.Column("show_nutrition", sa.Boolean(), nullable=True),
|
||||
sa.Column("show_assets", sa.Boolean(), nullable=True),
|
||||
sa.Column("landscape_view", sa.Boolean(), nullable=True),
|
||||
sa.Column("disable_amount", sa.Boolean(), nullable=True),
|
||||
sa.Column("disable_comments", sa.Boolean(), nullable=True),
|
||||
sa.Column("locked", sa.Boolean(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["recipe_id"],
|
||||
["recipes.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_table(
|
||||
"recipe_share_tokens",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("expires_at", sa.DateTime(), nullable=False),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_id"],
|
||||
["groups.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["recipe_id"],
|
||||
["recipes.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_recipe_share_tokens_group_id"), "recipe_share_tokens", ["group_id"], unique=False)
|
||||
op.create_table(
|
||||
"recipes_to_categories",
|
||||
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("category_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["category_id"],
|
||||
["categories.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["recipe_id"],
|
||||
["recipes.id"],
|
||||
),
|
||||
)
|
||||
op.create_table(
|
||||
"recipes_to_tags",
|
||||
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("tag_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["recipe_id"],
|
||||
["recipes.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["tag_id"],
|
||||
["tags.id"],
|
||||
),
|
||||
)
|
||||
op.create_table(
|
||||
"recipes_to_tools",
|
||||
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("tool_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["recipe_id"],
|
||||
["recipes.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["tool_id"],
|
||||
["tools.id"],
|
||||
),
|
||||
)
|
||||
op.create_table(
|
||||
"report_entries",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("success", sa.Boolean(), nullable=True),
|
||||
sa.Column("message", sa.String(), nullable=True),
|
||||
sa.Column("exception", sa.String(), nullable=True),
|
||||
sa.Column("timestamp", sa.DateTime(), nullable=False),
|
||||
sa.Column("report_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.ForeignKeyConstraint(
|
||||
["report_id"],
|
||||
["group_reports.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_table(
|
||||
"shopping_list_recipe_reference",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("shopping_list_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("recipe_quantity", sa.Float(), nullable=False),
|
||||
sa.ForeignKeyConstraint(
|
||||
["recipe_id"],
|
||||
["recipes.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["shopping_list_id"],
|
||||
["shopping_lists.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id", "shopping_list_id"),
|
||||
)
|
||||
op.create_index(
|
||||
op.f("ix_shopping_list_recipe_reference_recipe_id"),
|
||||
"shopping_list_recipe_reference",
|
||||
["recipe_id"],
|
||||
unique=False,
|
||||
)
|
||||
op.create_table(
|
||||
"users",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("full_name", sa.String(), nullable=True),
|
||||
sa.Column("username", sa.String(), nullable=True),
|
||||
sa.Column("email", sa.String(), nullable=True),
|
||||
sa.Column("password", sa.String(), nullable=True),
|
||||
sa.Column("admin", sa.Boolean(), nullable=True),
|
||||
sa.Column("advanced", sa.Boolean(), nullable=True),
|
||||
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("cache_key", sa.String(), nullable=True),
|
||||
sa.Column("can_manage", sa.Boolean(), nullable=True),
|
||||
sa.Column("can_invite", sa.Boolean(), nullable=True),
|
||||
sa.Column("can_organize", sa.Boolean(), nullable=True),
|
||||
sa.Column("owned_recipes_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_id"],
|
||||
["groups.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["owned_recipes_id"],
|
||||
["recipes.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_users_email"), "users", ["email"], unique=True)
|
||||
op.create_index(op.f("ix_users_full_name"), "users", ["full_name"], unique=False)
|
||||
op.create_index(op.f("ix_users_group_id"), "users", ["group_id"], unique=False)
|
||||
op.create_index(op.f("ix_users_username"), "users", ["username"], unique=True)
|
||||
op.create_table(
|
||||
"long_live_tokens",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("name", sa.String(), nullable=False),
|
||||
sa.Column("token", sa.String(), nullable=False),
|
||||
sa.Column("user_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["user_id"],
|
||||
["users.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_table(
|
||||
"password_reset_tokens",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("user_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("token", sa.String(length=64), nullable=False),
|
||||
sa.ForeignKeyConstraint(
|
||||
["user_id"],
|
||||
["users.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("token"),
|
||||
)
|
||||
op.create_table(
|
||||
"recipe_comments",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("text", sa.String(), nullable=True),
|
||||
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("user_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.ForeignKeyConstraint(
|
||||
["recipe_id"],
|
||||
["recipes.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["user_id"],
|
||||
["users.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_table(
|
||||
"recipe_ingredient_ref_link",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("instruction_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("reference_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["instruction_id"],
|
||||
["recipe_instructions.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_table(
|
||||
"recipes_ingredients",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("position", sa.Integer(), nullable=True),
|
||||
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("title", sa.String(), nullable=True),
|
||||
sa.Column("note", sa.String(), nullable=True),
|
||||
sa.Column("unit_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("food_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("quantity", sa.Integer(), nullable=True),
|
||||
sa.Column("reference_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["food_id"],
|
||||
["ingredient_foods.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["recipe_id"],
|
||||
["recipes.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["unit_id"],
|
||||
["ingredient_units.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_table(
|
||||
"shopping_list_items",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("shopping_list_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("is_ingredient", sa.Boolean(), nullable=True),
|
||||
sa.Column("position", sa.Integer(), nullable=False),
|
||||
sa.Column("checked", sa.Boolean(), nullable=True),
|
||||
sa.Column("quantity", sa.Float(), nullable=True),
|
||||
sa.Column("note", sa.String(), nullable=True),
|
||||
sa.Column("is_food", sa.Boolean(), nullable=True),
|
||||
sa.Column("unit_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("food_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("label_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["food_id"],
|
||||
["ingredient_foods.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["label_id"],
|
||||
["multi_purpose_labels.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["shopping_list_id"],
|
||||
["shopping_lists.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["unit_id"],
|
||||
["ingredient_units.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_table(
|
||||
"users_to_favorites",
|
||||
sa.Column("user_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["recipe_id"],
|
||||
["recipes.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["user_id"],
|
||||
["users.id"],
|
||||
),
|
||||
)
|
||||
op.create_table(
|
||||
"shopping_list_item_recipe_reference",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("shopping_list_item_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("recipe_quantity", sa.Float(), nullable=False),
|
||||
sa.ForeignKeyConstraint(
|
||||
["recipe_id"],
|
||||
["recipes.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["shopping_list_item_id"],
|
||||
["shopping_list_items.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id", "shopping_list_item_id"),
|
||||
)
|
||||
op.create_index(
|
||||
op.f("ix_shopping_list_item_recipe_reference_recipe_id"),
|
||||
"shopping_list_item_recipe_reference",
|
||||
["recipe_id"],
|
||||
unique=False,
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_index(
|
||||
op.f("ix_shopping_list_item_recipe_reference_recipe_id"), table_name="shopping_list_item_recipe_reference"
|
||||
)
|
||||
op.drop_table("shopping_list_item_recipe_reference")
|
||||
op.drop_table("users_to_favorites")
|
||||
op.drop_table("shopping_list_items")
|
||||
op.drop_table("recipes_ingredients")
|
||||
op.drop_table("recipe_ingredient_ref_link")
|
||||
op.drop_table("recipe_comments")
|
||||
op.drop_table("password_reset_tokens")
|
||||
op.drop_table("long_live_tokens")
|
||||
op.drop_index(op.f("ix_users_username"), table_name="users")
|
||||
op.drop_index(op.f("ix_users_group_id"), table_name="users")
|
||||
op.drop_index(op.f("ix_users_full_name"), table_name="users")
|
||||
op.drop_index(op.f("ix_users_email"), table_name="users")
|
||||
op.drop_table("users")
|
||||
op.drop_index(op.f("ix_shopping_list_recipe_reference_recipe_id"), table_name="shopping_list_recipe_reference")
|
||||
op.drop_table("shopping_list_recipe_reference")
|
||||
op.drop_table("report_entries")
|
||||
op.drop_table("recipes_to_tools")
|
||||
op.drop_table("recipes_to_tags")
|
||||
op.drop_table("recipes_to_categories")
|
||||
op.drop_index(op.f("ix_recipe_share_tokens_group_id"), table_name="recipe_share_tokens")
|
||||
op.drop_table("recipe_share_tokens")
|
||||
op.drop_table("recipe_settings")
|
||||
op.drop_table("recipe_nutrition")
|
||||
op.drop_table("recipe_instructions")
|
||||
op.drop_table("recipe_assets")
|
||||
op.drop_table("plan_rules_to_tags")
|
||||
op.drop_table("plan_rules_to_categories")
|
||||
op.drop_table("notes")
|
||||
op.drop_table("ingredient_foods")
|
||||
op.drop_table("group_to_categories")
|
||||
op.drop_index(op.f("ix_group_meal_plans_title"), table_name="group_meal_plans")
|
||||
op.drop_index(op.f("ix_group_meal_plans_recipe_id"), table_name="group_meal_plans")
|
||||
op.drop_index(op.f("ix_group_meal_plans_group_id"), table_name="group_meal_plans")
|
||||
op.drop_index(op.f("ix_group_meal_plans_entry_type"), table_name="group_meal_plans")
|
||||
op.drop_index(op.f("ix_group_meal_plans_date"), table_name="group_meal_plans")
|
||||
op.drop_table("group_meal_plans")
|
||||
op.drop_table("group_events_notifier_options")
|
||||
op.drop_table("cookbooks_to_categories")
|
||||
op.drop_table("api_extras")
|
||||
op.drop_index(op.f("ix_webhook_urls_group_id"), table_name="webhook_urls")
|
||||
op.drop_table("webhook_urls")
|
||||
op.drop_index(op.f("ix_tools_slug"), table_name="tools")
|
||||
op.drop_index(op.f("ix_tools_name"), table_name="tools")
|
||||
op.drop_table("tools")
|
||||
op.drop_index(op.f("ix_tags_slug"), table_name="tags")
|
||||
op.drop_index(op.f("ix_tags_name"), table_name="tags")
|
||||
op.drop_index(op.f("ix_tags_group_id"), table_name="tags")
|
||||
op.drop_table("tags")
|
||||
op.drop_index(op.f("ix_shopping_lists_group_id"), table_name="shopping_lists")
|
||||
op.drop_table("shopping_lists")
|
||||
op.drop_index(op.f("ix_server_tasks_group_id"), table_name="server_tasks")
|
||||
op.drop_table("server_tasks")
|
||||
op.drop_index(op.f("ix_recipes_user_id"), table_name="recipes")
|
||||
op.drop_index(op.f("ix_recipes_slug"), table_name="recipes")
|
||||
op.drop_index(op.f("ix_recipes_group_id"), table_name="recipes")
|
||||
op.drop_table("recipes")
|
||||
op.drop_index(op.f("ix_multi_purpose_labels_group_id"), table_name="multi_purpose_labels")
|
||||
op.drop_table("multi_purpose_labels")
|
||||
op.drop_index(op.f("ix_invite_tokens_token"), table_name="invite_tokens")
|
||||
op.drop_table("invite_tokens")
|
||||
op.drop_table("ingredient_units")
|
||||
op.drop_index(op.f("ix_group_reports_group_id"), table_name="group_reports")
|
||||
op.drop_index(op.f("ix_group_reports_category"), table_name="group_reports")
|
||||
op.drop_table("group_reports")
|
||||
op.drop_index(op.f("ix_group_preferences_group_id"), table_name="group_preferences")
|
||||
op.drop_table("group_preferences")
|
||||
op.drop_table("group_meal_plan_rules")
|
||||
op.drop_index(op.f("ix_group_events_notifiers_group_id"), table_name="group_events_notifiers")
|
||||
op.drop_table("group_events_notifiers")
|
||||
op.drop_index(op.f("ix_group_data_exports_group_id"), table_name="group_data_exports")
|
||||
op.drop_table("group_data_exports")
|
||||
op.drop_table("cookbooks")
|
||||
op.drop_index(op.f("ix_categories_slug"), table_name="categories")
|
||||
op.drop_index(op.f("ix_categories_name"), table_name="categories")
|
||||
op.drop_index(op.f("ix_categories_group_id"), table_name="categories")
|
||||
op.drop_table("categories")
|
||||
op.drop_index(op.f("ix_groups_name"), table_name="groups")
|
||||
op.drop_table("groups")
|
||||
@@ -0,0 +1,52 @@
|
||||
"""convert quantity from integer to float
|
||||
|
||||
Revision ID: 263dd6707191
|
||||
Revises: 6b0f5f32d602
|
||||
Create Date: 2022-03-23 17:43:34.727829
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "263dd6707191"
|
||||
down_revision = "6b0f5f32d602"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def is_postgres():
|
||||
return op.get_context().dialect.name == "postgresql"
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
|
||||
# SQLite doesn't require migration as types are not enforced.
|
||||
# Postgres Specific Migration
|
||||
if is_postgres():
|
||||
op.alter_column(
|
||||
"recipes_ingredients",
|
||||
"quantity",
|
||||
type_=sa.Float(),
|
||||
existing_type=sa.Integer(),
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
|
||||
# SQLite doesn't require migration as types are not enforced.
|
||||
# Postgres Specific Migration
|
||||
if is_postgres():
|
||||
op.alter_column(
|
||||
"recipes_ingredients",
|
||||
"quantity",
|
||||
type_=sa.Integer(),
|
||||
existing_type=sa.Float(),
|
||||
)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,25 @@
|
||||
"""Add original_text column to recipes_ingredients
|
||||
|
||||
Revision ID: f1a2dbee5fe9
|
||||
Revises: 263dd6707191
|
||||
Create Date: 2022-03-27 19:30:28.545846
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "f1a2dbee5fe9"
|
||||
down_revision = "263dd6707191"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column("recipes_ingredients", sa.Column("original_text", sa.String(), nullable=True))
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_column("recipes_ingredients", "original_text")
|
||||
@@ -0,0 +1,58 @@
|
||||
"""add tags to cookbooks
|
||||
|
||||
Revision ID: 59eb59135381
|
||||
Revises: f1a2dbee5fe9
|
||||
Create Date: 2022-03-31 19:19:55.428965
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
import mealie.db.migration_types
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "59eb59135381"
|
||||
down_revision = "f1a2dbee5fe9"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table(
|
||||
"cookbooks_to_tags",
|
||||
sa.Column("cookbook_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("tag_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["cookbook_id"],
|
||||
["cookbooks.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["tag_id"],
|
||||
["tags.id"],
|
||||
),
|
||||
)
|
||||
op.create_table(
|
||||
"cookbooks_to_tools",
|
||||
sa.Column("cookbook_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("tool_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["cookbook_id"],
|
||||
["cookbooks.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["tool_id"],
|
||||
["tools.id"],
|
||||
),
|
||||
)
|
||||
op.add_column("cookbooks", sa.Column("public", sa.Boolean(), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column("cookbooks", "public")
|
||||
op.drop_table("cookbooks_to_tools")
|
||||
op.drop_table("cookbooks_to_tags")
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,46 @@
|
||||
"""add require_all for cookbook filters
|
||||
|
||||
Revision ID: 09dfc897ad62
|
||||
Revises: 59eb59135381
|
||||
Create Date: 2022-04-03 10:48:51.379968
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
import mealie.db.migration_types # noqa: F401
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "09dfc897ad62"
|
||||
down_revision = "59eb59135381"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column("cookbooks", sa.Column("require_all_categories", sa.Boolean(), nullable=True))
|
||||
op.add_column("cookbooks", sa.Column("require_all_tags", sa.Boolean(), nullable=True))
|
||||
op.add_column("cookbooks", sa.Column("require_all_tools", sa.Boolean(), nullable=True))
|
||||
|
||||
# Set Defaults for Existing Cookbooks
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE cookbooks
|
||||
SET require_all_categories = TRUE,
|
||||
require_all_tags = TRUE,
|
||||
require_all_tools = TRUE
|
||||
"""
|
||||
)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column("cookbooks", "require_all_tools")
|
||||
op.drop_column("cookbooks", "require_all_tags")
|
||||
op.drop_column("cookbooks", "require_all_categories")
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,31 @@
|
||||
"""Add use_abbreviation column to ingredients
|
||||
|
||||
Revision ID: ab0bae02578f
|
||||
Revises: 09dfc897ad62
|
||||
Create Date: 2022-06-01 11:12:06.748383
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "ab0bae02578f"
|
||||
down_revision = "09dfc897ad62"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column("ingredient_units", sa.Column("use_abbreviation", sa.Boolean(), nullable=True))
|
||||
|
||||
op.execute("UPDATE ingredient_units SET use_abbreviation = FALSE WHERE use_abbreviation IS NULL")
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column("ingredient_units", "use_abbreviation")
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,32 @@
|
||||
"""add new webhook fields
|
||||
|
||||
|
||||
Revision ID: f30cf048c228
|
||||
Revises: ab0bae02578f
|
||||
Create Date: 2022-06-15 21:05:34.851857
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "f30cf048c228"
|
||||
down_revision = "ab0bae02578f"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column("webhook_urls", sa.Column("webhook_type", sa.String(), nullable=True))
|
||||
op.add_column("webhook_urls", sa.Column("scheduled_time", sa.Time(), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column("webhook_urls", "scheduled_time")
|
||||
op.drop_column("webhook_urls", "webhook_type")
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,27 @@
|
||||
"""add login_attemps and locked_at field to user table
|
||||
|
||||
Revision ID: 188374910655
|
||||
Revises: f30cf048c228
|
||||
Create Date: 2022-08-12 19:05:59.776361
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "188374910655"
|
||||
down_revision = "f30cf048c228"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column("users", sa.Column("login_attemps", sa.Integer(), nullable=True))
|
||||
op.add_column("users", sa.Column("locked_at", sa.DateTime(), nullable=True))
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_column("users", "locked_at")
|
||||
op.drop_column("users", "login_attemps")
|
||||
@@ -0,0 +1,28 @@
|
||||
"""Add is_ocr_recipe column to recipes
|
||||
|
||||
Revision ID: 089bfa50d0ed
|
||||
Revises: 188374910655
|
||||
Create Date: 2022-08-05 17:07:07.389271
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "089bfa50d0ed"
|
||||
down_revision = "188374910655"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column("recipes", sa.Column("is_ocr_recipe", sa.Boolean(), default=False, nullable=True))
|
||||
op.execute("UPDATE recipes SET is_ocr_recipe = FALSE")
|
||||
# SQLITE does not support ALTER COLUMN, so the column will stay nullable to prevent making this migration a mess
|
||||
# The Recipe pydantic model and the SQL server use False as default value anyway for this column so Null should be a very rare sight
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_column("recipes", "is_ocr_recipe")
|
||||
@@ -0,0 +1,73 @@
|
||||
"""add extras to shopping lists, list items, and ingredient foods
|
||||
|
||||
Revision ID: 44e8d670719d
|
||||
Revises: 089bfa50d0ed
|
||||
Create Date: 2022-08-29 13:57:40.452245
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
import mealie.db.migration_types
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "44e8d670719d"
|
||||
down_revision = "089bfa50d0ed"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table(
|
||||
"shopping_list_extras",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("key_name", sa.String(), nullable=True),
|
||||
sa.Column("value", sa.String(), nullable=True),
|
||||
sa.Column("shopping_list_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["shopping_list_id"],
|
||||
["shopping_lists.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_table(
|
||||
"ingredient_food_extras",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("key_name", sa.String(), nullable=True),
|
||||
sa.Column("value", sa.String(), nullable=True),
|
||||
sa.Column("ingredient_food_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["ingredient_food_id"],
|
||||
["ingredient_foods.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_table(
|
||||
"shopping_list_item_extras",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("key_name", sa.String(), nullable=True),
|
||||
sa.Column("value", sa.String(), nullable=True),
|
||||
sa.Column("shopping_list_item_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["shopping_list_item_id"],
|
||||
["shopping_list_items.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table("shopping_list_item_extras")
|
||||
op.drop_table("ingredient_food_extras")
|
||||
op.drop_table("shopping_list_extras")
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,51 @@
|
||||
"""add recipe_timeline_events table
|
||||
|
||||
Revision ID: 2ea7a807915c
|
||||
Revises: 44e8d670719d
|
||||
Create Date: 2022-09-27 14:53:14.111054
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
import mealie.db.migration_types
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "2ea7a807915c"
|
||||
down_revision = "44e8d670719d"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table(
|
||||
"recipe_timeline_events",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("user_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("subject", sa.String(), nullable=False),
|
||||
sa.Column("message", sa.String(), nullable=True),
|
||||
sa.Column("event_type", sa.String(), nullable=True),
|
||||
sa.Column("image", sa.String(), nullable=True),
|
||||
sa.Column("timestamp", sa.DateTime(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["recipe_id"],
|
||||
["recipes.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["user_id"],
|
||||
["users.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table("recipe_timeline_events")
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,29 @@
|
||||
"""added recipe last made timestamp
|
||||
|
||||
Revision ID: 1923519381ad
|
||||
Revises: 2ea7a807915c
|
||||
Create Date: 2022-11-03 13:10:24.811134
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "1923519381ad"
|
||||
down_revision = "2ea7a807915c"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column("recipes", sa.Column("last_made", sa.DateTime(), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column("recipes", "last_made")
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,30 @@
|
||||
"""add recipe_scale to shopping list item ref
|
||||
|
||||
Revision ID: 167eb69066ad
|
||||
Revises: 1923519381ad
|
||||
Create Date: 2022-11-22 03:42:45.494567
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "167eb69066ad"
|
||||
down_revision = "1923519381ad"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column("shopping_list_item_recipe_reference", sa.Column("recipe_scale", sa.Float(), nullable=True))
|
||||
op.execute("UPDATE shopping_list_item_recipe_reference SET recipe_scale = 1")
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column("shopping_list_item_recipe_reference", "recipe_scale")
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,44 @@
|
||||
"""add related user to mealplan
|
||||
|
||||
Revision ID: 165d943c64ee
|
||||
Revises: 167eb69066ad
|
||||
Create Date: 2023-01-21 16:54:44.368768
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
import mealie.db.migration_types
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "165d943c64ee"
|
||||
down_revision = "167eb69066ad"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table("group_meal_plans", schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column("user_id", mealie.db.migration_types.GUID(), nullable=True))
|
||||
batch_op.create_index(batch_op.f("ix_group_meal_plans_user_id"), ["user_id"], unique=False)
|
||||
batch_op.create_foreign_key("fk_user_mealplans", "users", ["user_id"], ["id"])
|
||||
|
||||
with op.batch_alter_table("shopping_list_item_recipe_reference", schema=None) as batch_op:
|
||||
batch_op.alter_column("recipe_scale", existing_type=sa.FLOAT(), nullable=False)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table("shopping_list_item_recipe_reference", schema=None) as batch_op:
|
||||
batch_op.alter_column("recipe_scale", existing_type=sa.FLOAT(), nullable=True)
|
||||
|
||||
with op.batch_alter_table("group_meal_plans", schema=None) as batch_op:
|
||||
batch_op.drop_constraint("fk_user_mealplans", type_="foreignkey")
|
||||
batch_op.drop_index(batch_op.f("ix_group_meal_plans_user_id"))
|
||||
batch_op.drop_column("user_id")
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,280 @@
|
||||
"""add missing foreign key and order indices
|
||||
|
||||
Revision ID: ff5f73b01a7a
|
||||
Revises: 165d943c64ee
|
||||
Create Date: 2023-02-07 20:57:21.066927
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "ff5f73b01a7a"
|
||||
down_revision = "165d943c64ee"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_index(op.f("ix_api_extras_created_at"), "api_extras", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_api_extras_recipee_id"), "api_extras", ["recipee_id"], unique=False)
|
||||
op.create_index(op.f("ix_categories_created_at"), "categories", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_cookbooks_created_at"), "cookbooks", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_cookbooks_group_id"), "cookbooks", ["group_id"], unique=False)
|
||||
op.create_index(op.f("ix_cookbooks_slug"), "cookbooks", ["slug"], unique=False)
|
||||
op.create_index(
|
||||
op.f("ix_cookbooks_to_categories_category_id"), "cookbooks_to_categories", ["category_id"], unique=False
|
||||
)
|
||||
op.create_index(
|
||||
op.f("ix_cookbooks_to_categories_cookbook_id"), "cookbooks_to_categories", ["cookbook_id"], unique=False
|
||||
)
|
||||
op.create_index(op.f("ix_cookbooks_to_tags_cookbook_id"), "cookbooks_to_tags", ["cookbook_id"], unique=False)
|
||||
op.create_index(op.f("ix_cookbooks_to_tags_tag_id"), "cookbooks_to_tags", ["tag_id"], unique=False)
|
||||
op.create_index(op.f("ix_cookbooks_to_tools_cookbook_id"), "cookbooks_to_tools", ["cookbook_id"], unique=False)
|
||||
op.create_index(op.f("ix_cookbooks_to_tools_tool_id"), "cookbooks_to_tools", ["tool_id"], unique=False)
|
||||
op.create_index(op.f("ix_group_data_exports_created_at"), "group_data_exports", ["created_at"], unique=False)
|
||||
op.create_index(
|
||||
op.f("ix_group_events_notifier_options_created_at"),
|
||||
"group_events_notifier_options",
|
||||
["created_at"],
|
||||
unique=False,
|
||||
)
|
||||
op.create_index(
|
||||
op.f("ix_group_events_notifiers_created_at"), "group_events_notifiers", ["created_at"], unique=False
|
||||
)
|
||||
op.create_index(op.f("ix_group_meal_plan_rules_created_at"), "group_meal_plan_rules", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_group_meal_plan_rules_group_id"), "group_meal_plan_rules", ["group_id"], unique=False)
|
||||
op.create_index(op.f("ix_group_meal_plans_created_at"), "group_meal_plans", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_group_preferences_created_at"), "group_preferences", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_group_reports_created_at"), "group_reports", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_group_to_categories_category_id"), "group_to_categories", ["category_id"], unique=False)
|
||||
op.create_index(op.f("ix_group_to_categories_group_id"), "group_to_categories", ["group_id"], unique=False)
|
||||
op.create_index(op.f("ix_groups_created_at"), "groups", ["created_at"], unique=False)
|
||||
op.create_index(
|
||||
op.f("ix_ingredient_food_extras_created_at"), "ingredient_food_extras", ["created_at"], unique=False
|
||||
)
|
||||
op.create_index(
|
||||
op.f("ix_ingredient_food_extras_ingredient_food_id"),
|
||||
"ingredient_food_extras",
|
||||
["ingredient_food_id"],
|
||||
unique=False,
|
||||
)
|
||||
op.create_index(op.f("ix_ingredient_foods_created_at"), "ingredient_foods", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_ingredient_foods_group_id"), "ingredient_foods", ["group_id"], unique=False)
|
||||
op.create_index(op.f("ix_ingredient_foods_label_id"), "ingredient_foods", ["label_id"], unique=False)
|
||||
op.create_index(op.f("ix_ingredient_units_created_at"), "ingredient_units", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_ingredient_units_group_id"), "ingredient_units", ["group_id"], unique=False)
|
||||
op.create_index(op.f("ix_invite_tokens_created_at"), "invite_tokens", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_invite_tokens_group_id"), "invite_tokens", ["group_id"], unique=False)
|
||||
op.create_index(op.f("ix_long_live_tokens_created_at"), "long_live_tokens", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_long_live_tokens_token"), "long_live_tokens", ["token"], unique=False)
|
||||
op.create_index(op.f("ix_long_live_tokens_user_id"), "long_live_tokens", ["user_id"], unique=False)
|
||||
op.create_index(op.f("ix_multi_purpose_labels_created_at"), "multi_purpose_labels", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_notes_created_at"), "notes", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_notes_recipe_id"), "notes", ["recipe_id"], unique=False)
|
||||
op.create_index(op.f("ix_password_reset_tokens_created_at"), "password_reset_tokens", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_password_reset_tokens_user_id"), "password_reset_tokens", ["user_id"], unique=False)
|
||||
op.create_index(
|
||||
op.f("ix_plan_rules_to_categories_category_id"), "plan_rules_to_categories", ["category_id"], unique=False
|
||||
)
|
||||
op.create_index(
|
||||
op.f("ix_plan_rules_to_categories_group_plan_rule_id"),
|
||||
"plan_rules_to_categories",
|
||||
["group_plan_rule_id"],
|
||||
unique=False,
|
||||
)
|
||||
op.create_index(op.f("ix_plan_rules_to_tags_plan_rule_id"), "plan_rules_to_tags", ["plan_rule_id"], unique=False)
|
||||
op.create_index(op.f("ix_plan_rules_to_tags_tag_id"), "plan_rules_to_tags", ["tag_id"], unique=False)
|
||||
op.create_index(op.f("ix_recipe_assets_created_at"), "recipe_assets", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_recipe_assets_recipe_id"), "recipe_assets", ["recipe_id"], unique=False)
|
||||
op.create_index(op.f("ix_recipe_comments_created_at"), "recipe_comments", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_recipe_comments_recipe_id"), "recipe_comments", ["recipe_id"], unique=False)
|
||||
op.create_index(op.f("ix_recipe_comments_user_id"), "recipe_comments", ["user_id"], unique=False)
|
||||
op.create_index(
|
||||
op.f("ix_recipe_ingredient_ref_link_created_at"), "recipe_ingredient_ref_link", ["created_at"], unique=False
|
||||
)
|
||||
op.create_index(
|
||||
op.f("ix_recipe_ingredient_ref_link_instruction_id"),
|
||||
"recipe_ingredient_ref_link",
|
||||
["instruction_id"],
|
||||
unique=False,
|
||||
)
|
||||
op.create_index(
|
||||
op.f("ix_recipe_ingredient_ref_link_reference_id"), "recipe_ingredient_ref_link", ["reference_id"], unique=False
|
||||
)
|
||||
op.create_index(op.f("ix_recipe_instructions_created_at"), "recipe_instructions", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_recipe_instructions_position"), "recipe_instructions", ["position"], unique=False)
|
||||
op.create_index(op.f("ix_recipe_instructions_recipe_id"), "recipe_instructions", ["recipe_id"], unique=False)
|
||||
op.create_index(op.f("ix_recipe_nutrition_created_at"), "recipe_nutrition", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_recipe_nutrition_recipe_id"), "recipe_nutrition", ["recipe_id"], unique=False)
|
||||
op.create_index(op.f("ix_recipe_settings_created_at"), "recipe_settings", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_recipe_settings_recipe_id"), "recipe_settings", ["recipe_id"], unique=False)
|
||||
op.create_index(op.f("ix_recipe_share_tokens_created_at"), "recipe_share_tokens", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_recipe_share_tokens_recipe_id"), "recipe_share_tokens", ["recipe_id"], unique=False)
|
||||
op.create_index(
|
||||
op.f("ix_recipe_timeline_events_created_at"), "recipe_timeline_events", ["created_at"], unique=False
|
||||
)
|
||||
op.create_index(op.f("ix_recipe_timeline_events_recipe_id"), "recipe_timeline_events", ["recipe_id"], unique=False)
|
||||
op.create_index(op.f("ix_recipe_timeline_events_timestamp"), "recipe_timeline_events", ["timestamp"], unique=False)
|
||||
op.create_index(op.f("ix_recipe_timeline_events_user_id"), "recipe_timeline_events", ["user_id"], unique=False)
|
||||
op.create_index(op.f("ix_recipes_created_at"), "recipes", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_recipes_name"), "recipes", ["name"], unique=False)
|
||||
op.create_index(op.f("ix_recipes_ingredients_created_at"), "recipes_ingredients", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_recipes_ingredients_food_id"), "recipes_ingredients", ["food_id"], unique=False)
|
||||
op.create_index(op.f("ix_recipes_ingredients_position"), "recipes_ingredients", ["position"], unique=False)
|
||||
op.create_index(op.f("ix_recipes_ingredients_unit_id"), "recipes_ingredients", ["unit_id"], unique=False)
|
||||
op.create_index(
|
||||
op.f("ix_recipes_to_categories_category_id"), "recipes_to_categories", ["category_id"], unique=False
|
||||
)
|
||||
op.create_index(op.f("ix_recipes_to_categories_recipe_id"), "recipes_to_categories", ["recipe_id"], unique=False)
|
||||
op.create_index(op.f("ix_recipes_to_tags_recipe_id"), "recipes_to_tags", ["recipe_id"], unique=False)
|
||||
op.create_index(op.f("ix_recipes_to_tags_tag_id"), "recipes_to_tags", ["tag_id"], unique=False)
|
||||
op.create_index(op.f("ix_recipes_to_tools_recipe_id"), "recipes_to_tools", ["recipe_id"], unique=False)
|
||||
op.create_index(op.f("ix_recipes_to_tools_tool_id"), "recipes_to_tools", ["tool_id"], unique=False)
|
||||
op.create_index(op.f("ix_report_entries_created_at"), "report_entries", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_report_entries_report_id"), "report_entries", ["report_id"], unique=False)
|
||||
op.create_index(op.f("ix_server_tasks_created_at"), "server_tasks", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_shopping_list_extras_created_at"), "shopping_list_extras", ["created_at"], unique=False)
|
||||
op.create_index(
|
||||
op.f("ix_shopping_list_extras_shopping_list_id"), "shopping_list_extras", ["shopping_list_id"], unique=False
|
||||
)
|
||||
op.create_index(
|
||||
op.f("ix_shopping_list_item_extras_created_at"), "shopping_list_item_extras", ["created_at"], unique=False
|
||||
)
|
||||
op.create_index(
|
||||
op.f("ix_shopping_list_item_extras_shopping_list_item_id"),
|
||||
"shopping_list_item_extras",
|
||||
["shopping_list_item_id"],
|
||||
unique=False,
|
||||
)
|
||||
op.create_index(
|
||||
op.f("ix_shopping_list_item_recipe_reference_created_at"),
|
||||
"shopping_list_item_recipe_reference",
|
||||
["created_at"],
|
||||
unique=False,
|
||||
)
|
||||
op.create_index(op.f("ix_shopping_list_items_created_at"), "shopping_list_items", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_shopping_list_items_position"), "shopping_list_items", ["position"], unique=False)
|
||||
op.create_index(
|
||||
op.f("ix_shopping_list_items_shopping_list_id"), "shopping_list_items", ["shopping_list_id"], unique=False
|
||||
)
|
||||
op.create_index(
|
||||
op.f("ix_shopping_list_recipe_reference_created_at"),
|
||||
"shopping_list_recipe_reference",
|
||||
["created_at"],
|
||||
unique=False,
|
||||
)
|
||||
op.create_index(op.f("ix_shopping_lists_created_at"), "shopping_lists", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_tags_created_at"), "tags", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_tools_created_at"), "tools", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_tools_group_id"), "tools", ["group_id"], unique=False)
|
||||
op.create_index(op.f("ix_users_created_at"), "users", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_users_to_favorites_recipe_id"), "users_to_favorites", ["recipe_id"], unique=False)
|
||||
op.create_index(op.f("ix_users_to_favorites_user_id"), "users_to_favorites", ["user_id"], unique=False)
|
||||
op.create_index(op.f("ix_webhook_urls_created_at"), "webhook_urls", ["created_at"], unique=False)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index(op.f("ix_webhook_urls_created_at"), table_name="webhook_urls")
|
||||
op.drop_index(op.f("ix_users_to_favorites_user_id"), table_name="users_to_favorites")
|
||||
op.drop_index(op.f("ix_users_to_favorites_recipe_id"), table_name="users_to_favorites")
|
||||
op.drop_index(op.f("ix_users_created_at"), table_name="users")
|
||||
op.drop_index(op.f("ix_tools_group_id"), table_name="tools")
|
||||
op.drop_index(op.f("ix_tools_created_at"), table_name="tools")
|
||||
op.drop_index(op.f("ix_tags_created_at"), table_name="tags")
|
||||
op.drop_index(op.f("ix_shopping_lists_created_at"), table_name="shopping_lists")
|
||||
op.drop_index(op.f("ix_shopping_list_recipe_reference_created_at"), table_name="shopping_list_recipe_reference")
|
||||
op.drop_index(op.f("ix_shopping_list_items_shopping_list_id"), table_name="shopping_list_items")
|
||||
op.drop_index(op.f("ix_shopping_list_items_position"), table_name="shopping_list_items")
|
||||
op.drop_index(op.f("ix_shopping_list_items_created_at"), table_name="shopping_list_items")
|
||||
op.drop_index(
|
||||
op.f("ix_shopping_list_item_recipe_reference_created_at"), table_name="shopping_list_item_recipe_reference"
|
||||
)
|
||||
op.drop_index(op.f("ix_shopping_list_item_extras_shopping_list_item_id"), table_name="shopping_list_item_extras")
|
||||
op.drop_index(op.f("ix_shopping_list_item_extras_created_at"), table_name="shopping_list_item_extras")
|
||||
op.drop_index(op.f("ix_shopping_list_extras_shopping_list_id"), table_name="shopping_list_extras")
|
||||
op.drop_index(op.f("ix_shopping_list_extras_created_at"), table_name="shopping_list_extras")
|
||||
op.drop_index(op.f("ix_server_tasks_created_at"), table_name="server_tasks")
|
||||
op.drop_index(op.f("ix_report_entries_report_id"), table_name="report_entries")
|
||||
op.drop_index(op.f("ix_report_entries_created_at"), table_name="report_entries")
|
||||
op.drop_index(op.f("ix_recipes_to_tools_tool_id"), table_name="recipes_to_tools")
|
||||
op.drop_index(op.f("ix_recipes_to_tools_recipe_id"), table_name="recipes_to_tools")
|
||||
op.drop_index(op.f("ix_recipes_to_tags_tag_id"), table_name="recipes_to_tags")
|
||||
op.drop_index(op.f("ix_recipes_to_tags_recipe_id"), table_name="recipes_to_tags")
|
||||
op.drop_index(op.f("ix_recipes_to_categories_recipe_id"), table_name="recipes_to_categories")
|
||||
op.drop_index(op.f("ix_recipes_to_categories_category_id"), table_name="recipes_to_categories")
|
||||
op.drop_index(op.f("ix_recipes_ingredients_unit_id"), table_name="recipes_ingredients")
|
||||
op.drop_index(op.f("ix_recipes_ingredients_position"), table_name="recipes_ingredients")
|
||||
op.drop_index(op.f("ix_recipes_ingredients_food_id"), table_name="recipes_ingredients")
|
||||
op.drop_index(op.f("ix_recipes_ingredients_created_at"), table_name="recipes_ingredients")
|
||||
op.drop_index(op.f("ix_recipes_name"), table_name="recipes")
|
||||
op.drop_index(op.f("ix_recipes_created_at"), table_name="recipes")
|
||||
op.drop_index(op.f("ix_recipe_timeline_events_user_id"), table_name="recipe_timeline_events")
|
||||
op.drop_index(op.f("ix_recipe_timeline_events_timestamp"), table_name="recipe_timeline_events")
|
||||
op.drop_index(op.f("ix_recipe_timeline_events_recipe_id"), table_name="recipe_timeline_events")
|
||||
op.drop_index(op.f("ix_recipe_timeline_events_created_at"), table_name="recipe_timeline_events")
|
||||
op.drop_index(op.f("ix_recipe_share_tokens_recipe_id"), table_name="recipe_share_tokens")
|
||||
op.drop_index(op.f("ix_recipe_share_tokens_created_at"), table_name="recipe_share_tokens")
|
||||
op.drop_index(op.f("ix_recipe_settings_recipe_id"), table_name="recipe_settings")
|
||||
op.drop_index(op.f("ix_recipe_settings_created_at"), table_name="recipe_settings")
|
||||
op.drop_index(op.f("ix_recipe_nutrition_recipe_id"), table_name="recipe_nutrition")
|
||||
op.drop_index(op.f("ix_recipe_nutrition_created_at"), table_name="recipe_nutrition")
|
||||
op.drop_index(op.f("ix_recipe_instructions_recipe_id"), table_name="recipe_instructions")
|
||||
op.drop_index(op.f("ix_recipe_instructions_position"), table_name="recipe_instructions")
|
||||
op.drop_index(op.f("ix_recipe_instructions_created_at"), table_name="recipe_instructions")
|
||||
op.drop_index(op.f("ix_recipe_ingredient_ref_link_reference_id"), table_name="recipe_ingredient_ref_link")
|
||||
op.drop_index(op.f("ix_recipe_ingredient_ref_link_instruction_id"), table_name="recipe_ingredient_ref_link")
|
||||
op.drop_index(op.f("ix_recipe_ingredient_ref_link_created_at"), table_name="recipe_ingredient_ref_link")
|
||||
op.drop_index(op.f("ix_recipe_comments_user_id"), table_name="recipe_comments")
|
||||
op.drop_index(op.f("ix_recipe_comments_recipe_id"), table_name="recipe_comments")
|
||||
op.drop_index(op.f("ix_recipe_comments_created_at"), table_name="recipe_comments")
|
||||
op.drop_index(op.f("ix_recipe_assets_recipe_id"), table_name="recipe_assets")
|
||||
op.drop_index(op.f("ix_recipe_assets_created_at"), table_name="recipe_assets")
|
||||
op.drop_index(op.f("ix_plan_rules_to_tags_tag_id"), table_name="plan_rules_to_tags")
|
||||
op.drop_index(op.f("ix_plan_rules_to_tags_plan_rule_id"), table_name="plan_rules_to_tags")
|
||||
op.drop_index(op.f("ix_plan_rules_to_categories_group_plan_rule_id"), table_name="plan_rules_to_categories")
|
||||
op.drop_index(op.f("ix_plan_rules_to_categories_category_id"), table_name="plan_rules_to_categories")
|
||||
op.drop_index(op.f("ix_password_reset_tokens_user_id"), table_name="password_reset_tokens")
|
||||
op.drop_index(op.f("ix_password_reset_tokens_created_at"), table_name="password_reset_tokens")
|
||||
op.drop_index(op.f("ix_notes_recipe_id"), table_name="notes")
|
||||
op.drop_index(op.f("ix_notes_created_at"), table_name="notes")
|
||||
op.drop_index(op.f("ix_multi_purpose_labels_created_at"), table_name="multi_purpose_labels")
|
||||
op.drop_index(op.f("ix_long_live_tokens_user_id"), table_name="long_live_tokens")
|
||||
op.drop_index(op.f("ix_long_live_tokens_token"), table_name="long_live_tokens")
|
||||
op.drop_index(op.f("ix_long_live_tokens_created_at"), table_name="long_live_tokens")
|
||||
op.drop_index(op.f("ix_invite_tokens_group_id"), table_name="invite_tokens")
|
||||
op.drop_index(op.f("ix_invite_tokens_created_at"), table_name="invite_tokens")
|
||||
op.drop_index(op.f("ix_ingredient_units_group_id"), table_name="ingredient_units")
|
||||
op.drop_index(op.f("ix_ingredient_units_created_at"), table_name="ingredient_units")
|
||||
op.drop_index(op.f("ix_ingredient_foods_label_id"), table_name="ingredient_foods")
|
||||
op.drop_index(op.f("ix_ingredient_foods_group_id"), table_name="ingredient_foods")
|
||||
op.drop_index(op.f("ix_ingredient_foods_created_at"), table_name="ingredient_foods")
|
||||
op.drop_index(op.f("ix_ingredient_food_extras_ingredient_food_id"), table_name="ingredient_food_extras")
|
||||
op.drop_index(op.f("ix_ingredient_food_extras_created_at"), table_name="ingredient_food_extras")
|
||||
op.drop_index(op.f("ix_groups_created_at"), table_name="groups")
|
||||
op.drop_index(op.f("ix_group_to_categories_group_id"), table_name="group_to_categories")
|
||||
op.drop_index(op.f("ix_group_to_categories_category_id"), table_name="group_to_categories")
|
||||
op.drop_index(op.f("ix_group_reports_created_at"), table_name="group_reports")
|
||||
op.drop_index(op.f("ix_group_preferences_created_at"), table_name="group_preferences")
|
||||
op.drop_index(op.f("ix_group_meal_plans_created_at"), table_name="group_meal_plans")
|
||||
op.drop_index(op.f("ix_group_meal_plan_rules_group_id"), table_name="group_meal_plan_rules")
|
||||
op.drop_index(op.f("ix_group_meal_plan_rules_created_at"), table_name="group_meal_plan_rules")
|
||||
op.drop_index(op.f("ix_group_events_notifiers_created_at"), table_name="group_events_notifiers")
|
||||
op.drop_index(op.f("ix_group_events_notifier_options_created_at"), table_name="group_events_notifier_options")
|
||||
op.drop_index(op.f("ix_group_data_exports_created_at"), table_name="group_data_exports")
|
||||
op.drop_index(op.f("ix_cookbooks_to_tools_tool_id"), table_name="cookbooks_to_tools")
|
||||
op.drop_index(op.f("ix_cookbooks_to_tools_cookbook_id"), table_name="cookbooks_to_tools")
|
||||
op.drop_index(op.f("ix_cookbooks_to_tags_tag_id"), table_name="cookbooks_to_tags")
|
||||
op.drop_index(op.f("ix_cookbooks_to_tags_cookbook_id"), table_name="cookbooks_to_tags")
|
||||
op.drop_index(op.f("ix_cookbooks_to_categories_cookbook_id"), table_name="cookbooks_to_categories")
|
||||
op.drop_index(op.f("ix_cookbooks_to_categories_category_id"), table_name="cookbooks_to_categories")
|
||||
op.drop_index(op.f("ix_cookbooks_slug"), table_name="cookbooks")
|
||||
op.drop_index(op.f("ix_cookbooks_group_id"), table_name="cookbooks")
|
||||
op.drop_index(op.f("ix_cookbooks_created_at"), table_name="cookbooks")
|
||||
op.drop_index(op.f("ix_categories_created_at"), table_name="categories")
|
||||
op.drop_index(op.f("ix_api_extras_recipee_id"), table_name="api_extras")
|
||||
op.drop_index(op.f("ix_api_extras_created_at"), table_name="api_extras")
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,35 @@
|
||||
"""add more indices necessary for search
|
||||
|
||||
Revision ID: 16160bf731a0
|
||||
Revises: ff5f73b01a7a
|
||||
Create Date: 2023-02-10 21:18:32.405130
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "16160bf731a0"
|
||||
down_revision = "ff5f73b01a7a"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_index(op.f("ix_recipe_instructions_text"), "recipe_instructions", ["text"], unique=False)
|
||||
op.create_index(op.f("ix_recipes_description"), "recipes", ["description"], unique=False)
|
||||
op.create_index(op.f("ix_recipes_ingredients_note"), "recipes_ingredients", ["note"], unique=False)
|
||||
op.create_index(
|
||||
op.f("ix_recipes_ingredients_original_text"), "recipes_ingredients", ["original_text"], unique=False
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index(op.f("ix_recipes_ingredients_original_text"), table_name="recipes_ingredients")
|
||||
op.drop_index(op.f("ix_recipes_ingredients_note"), table_name="recipes_ingredients")
|
||||
op.drop_index(op.f("ix_recipes_description"), table_name="recipes")
|
||||
op.drop_index(op.f("ix_recipe_instructions_text"), table_name="recipe_instructions")
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,133 @@
|
||||
"""add normalized search properties
|
||||
|
||||
Revision ID: 5ab195a474eb
|
||||
Revises: 16160bf731a0
|
||||
Create Date: 2023-02-14 20:45:41.102571
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import orm, select
|
||||
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
|
||||
from text_unidecode import unidecode
|
||||
|
||||
from alembic import op
|
||||
from mealie.db.models._model_utils.guid import GUID
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "5ab195a474eb"
|
||||
down_revision = "16160bf731a0"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
class SqlAlchemyBase(DeclarativeBase):
|
||||
pass
|
||||
|
||||
|
||||
# Intermediate table definitions
|
||||
class RecipeModel(SqlAlchemyBase):
|
||||
__tablename__ = "recipes"
|
||||
|
||||
id: Mapped[GUID] = mapped_column(GUID, primary_key=True, default=GUID.generate)
|
||||
name: Mapped[str] = mapped_column(sa.String, nullable=False)
|
||||
description: Mapped[str | None] = mapped_column(sa.String)
|
||||
name_normalized: Mapped[str] = mapped_column(sa.String, nullable=False, index=True)
|
||||
description_normalized: Mapped[str | None] = mapped_column(sa.String, index=True)
|
||||
|
||||
|
||||
class RecipeIngredient(SqlAlchemyBase):
|
||||
__tablename__ = "recipes_ingredients"
|
||||
|
||||
id: Mapped[int] = mapped_column(sa.Integer, primary_key=True)
|
||||
note: Mapped[str | None] = mapped_column(sa.String)
|
||||
original_text: Mapped[str | None] = mapped_column(sa.String)
|
||||
note_normalized: Mapped[str | None] = mapped_column(sa.String, index=True)
|
||||
original_text_normalized: Mapped[str | None] = mapped_column(sa.String, index=True)
|
||||
|
||||
|
||||
def do_data_migration():
|
||||
bind = op.get_bind()
|
||||
session = orm.Session(bind=bind)
|
||||
|
||||
recipes = session.execute(select(RecipeModel)).scalars().all()
|
||||
for recipe in recipes:
|
||||
if recipe.name is not None:
|
||||
session.execute(
|
||||
sa.text(
|
||||
f"UPDATE {RecipeModel.__tablename__} SET name_normalized=:name_normalized WHERE id=:id"
|
||||
).bindparams(name_normalized=unidecode(recipe.name).lower().strip(), id=recipe.id)
|
||||
)
|
||||
|
||||
if recipe.description is not None:
|
||||
session.execute(
|
||||
sa.text(
|
||||
f"UPDATE {RecipeModel.__tablename__} SET description_normalized=:description_normalized WHERE id=:id"
|
||||
).bindparams(description_normalized=unidecode(recipe.description).lower().strip(), id=recipe.id)
|
||||
)
|
||||
|
||||
ingredients = session.execute(select(RecipeIngredient)).scalars().all()
|
||||
for ingredient in ingredients:
|
||||
if ingredient.note is not None:
|
||||
session.execute(
|
||||
sa.text(
|
||||
f"UPDATE {RecipeIngredient.__tablename__} SET note_normalized=:note_normalized WHERE id=:id"
|
||||
).bindparams(note_normalized=unidecode(ingredient.note).lower().strip(), id=ingredient.id)
|
||||
)
|
||||
|
||||
if ingredient.original_text is not None:
|
||||
session.execute(
|
||||
sa.text(
|
||||
f"UPDATE {RecipeIngredient.__tablename__} SET original_text_normalized=:original_text_normalized WHERE id=:id"
|
||||
).bindparams(
|
||||
original_text_normalized=unidecode(ingredient.original_text).lower().strip(), id=ingredient.id
|
||||
)
|
||||
)
|
||||
session.commit()
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
|
||||
# Set column default first, since we do not have values here yet
|
||||
op.add_column("recipes", sa.Column("name_normalized", sa.String(), nullable=False, server_default=""))
|
||||
op.add_column("recipes", sa.Column("description_normalized", sa.String(), nullable=True))
|
||||
op.drop_index("ix_recipes_description", table_name="recipes")
|
||||
op.drop_index("ix_recipes_name", table_name="recipes")
|
||||
op.create_index(op.f("ix_recipes_description_normalized"), "recipes", ["description_normalized"], unique=False)
|
||||
op.create_index(op.f("ix_recipes_name_normalized"), "recipes", ["name_normalized"], unique=False)
|
||||
op.add_column("recipes_ingredients", sa.Column("note_normalized", sa.String(), nullable=True))
|
||||
op.add_column("recipes_ingredients", sa.Column("original_text_normalized", sa.String(), nullable=True))
|
||||
op.drop_index("ix_recipes_ingredients_note", table_name="recipes_ingredients")
|
||||
op.drop_index("ix_recipes_ingredients_original_text", table_name="recipes_ingredients")
|
||||
op.create_index(
|
||||
op.f("ix_recipes_ingredients_note_normalized"), "recipes_ingredients", ["note_normalized"], unique=False
|
||||
)
|
||||
op.create_index(
|
||||
op.f("ix_recipes_ingredients_original_text_normalized"),
|
||||
"recipes_ingredients",
|
||||
["original_text_normalized"],
|
||||
unique=False,
|
||||
)
|
||||
do_data_migration()
|
||||
# Remove server default now that column should be filled for all rows
|
||||
with op.batch_alter_table("recipes", schema=None) as batch_op:
|
||||
batch_op.alter_column("name_normalized", existing_type=sa.String(), server_default=None)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index(op.f("ix_recipes_ingredients_original_text_normalized"), table_name="recipes_ingredients")
|
||||
op.drop_index(op.f("ix_recipes_ingredients_note_normalized"), table_name="recipes_ingredients")
|
||||
op.create_index("ix_recipes_ingredients_original_text", "recipes_ingredients", ["original_text"], unique=False)
|
||||
op.create_index("ix_recipes_ingredients_note", "recipes_ingredients", ["note"], unique=False)
|
||||
op.drop_column("recipes_ingredients", "original_text_normalized")
|
||||
op.drop_column("recipes_ingredients", "note_normalized")
|
||||
op.drop_index(op.f("ix_recipes_name_normalized"), table_name="recipes")
|
||||
op.drop_index(op.f("ix_recipes_description_normalized"), table_name="recipes")
|
||||
op.create_index("ix_recipes_name", "recipes", ["name"], unique=False)
|
||||
op.create_index("ix_recipes_description", "recipes", ["description"], unique=False)
|
||||
op.drop_column("recipes", "description_normalized")
|
||||
op.drop_column("recipes", "name_normalized")
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,90 @@
|
||||
"""added shopping list label settings
|
||||
|
||||
Revision ID: b04a08da2108
|
||||
Revises: 5ab195a474eb
|
||||
Create Date: 2023-21-02 22:03:19.837244
|
||||
|
||||
"""
|
||||
|
||||
from uuid import uuid4
|
||||
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import orm
|
||||
|
||||
import mealie.db.migration_types
|
||||
from alembic import op
|
||||
from mealie.db.models._model_utils.guid import GUID
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "b04a08da2108"
|
||||
down_revision = "5ab195a474eb"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
# Intermediate table definitions
|
||||
class SqlAlchemyBase(orm.DeclarativeBase):
|
||||
pass
|
||||
|
||||
|
||||
class ShoppingList(SqlAlchemyBase):
|
||||
__tablename__ = "shopping_lists"
|
||||
|
||||
id: orm.Mapped[GUID] = orm.mapped_column(GUID, primary_key=True, default=GUID.generate)
|
||||
group_id: orm.Mapped[GUID] = orm.mapped_column(GUID, sa.ForeignKey("groups.id"), nullable=False, index=True)
|
||||
|
||||
|
||||
class MultiPurposeLabel(SqlAlchemyBase):
|
||||
__tablename__ = "multi_purpose_labels"
|
||||
|
||||
id: orm.Mapped[GUID] = orm.mapped_column(GUID, primary_key=True, default=GUID.generate)
|
||||
group_id: orm.Mapped[GUID] = orm.mapped_column(GUID, sa.ForeignKey("groups.id"), nullable=False, index=True)
|
||||
|
||||
|
||||
def populate_shopping_lists_multi_purpose_labels(
|
||||
shopping_lists_multi_purpose_labels_table: sa.Table, session: orm.Session
|
||||
):
|
||||
shopping_lists = session.query(ShoppingList).options(orm.load_only(ShoppingList.id, ShoppingList.group_id)).all()
|
||||
|
||||
shopping_lists_labels_data: list[dict] = []
|
||||
for shopping_list in shopping_lists:
|
||||
labels = session.query(MultiPurposeLabel).filter(MultiPurposeLabel.group_id == ShoppingList.group_id).all()
|
||||
for i, label in enumerate(labels):
|
||||
shopping_lists_labels_data.append(
|
||||
{"id": uuid4(), "shopping_list_id": shopping_list.id, "label_id": label.id, "position": i}
|
||||
)
|
||||
|
||||
op.bulk_insert(shopping_lists_multi_purpose_labels_table, shopping_lists_labels_data)
|
||||
session.commit()
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
shopping_lists_multi_purpose_labels_table = op.create_table(
|
||||
"shopping_lists_multi_purpose_labels",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("shopping_list_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("label_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("position", sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(
|
||||
["label_id"],
|
||||
["multi_purpose_labels.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["shopping_list_id"],
|
||||
["shopping_lists.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id", "shopping_list_id", "label_id"),
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
session = orm.Session(bind=op.get_bind())
|
||||
populate_shopping_lists_multi_purpose_labels(shopping_lists_multi_purpose_labels_table, session)
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table("shopping_lists_multi_purpose_labels")
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,43 @@
|
||||
"""add auth_method to user table
|
||||
|
||||
Revision ID: 38514b39a824
|
||||
Revises: b04a08da2108
|
||||
Create Date: 2023-02-22 21:45:52.900964
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "38514b39a824"
|
||||
down_revision = "b04a08da2108"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def is_postgres():
|
||||
return op.get_context().dialect.name == "postgresql"
|
||||
|
||||
|
||||
authMethod = sa.Enum("MEALIE", "LDAP", name="authmethod")
|
||||
|
||||
|
||||
def upgrade():
|
||||
if is_postgres():
|
||||
authMethod.create(op.get_bind())
|
||||
|
||||
op.add_column(
|
||||
"users",
|
||||
sa.Column("auth_method", authMethod, nullable=False, server_default="MEALIE"),
|
||||
)
|
||||
op.execute("UPDATE users SET auth_method = 'LDAP' WHERE password = 'LDAP'")
|
||||
|
||||
|
||||
def downgrade():
|
||||
with op.batch_alter_table("users", schema=None) as batch_op:
|
||||
batch_op.drop_column("auth_method")
|
||||
|
||||
if is_postgres():
|
||||
authMethod.drop(op.get_bind())
|
||||
@@ -0,0 +1,85 @@
|
||||
"""postgres fuzzy search
|
||||
|
||||
Revision ID: b3dbb554ba53
|
||||
Revises: 38514b39a824
|
||||
Create Date: 2023-04-13 06:47:04.617131
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "b3dbb554ba53"
|
||||
down_revision = "38514b39a824"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def get_db_type():
|
||||
return op.get_context().dialect.name
|
||||
|
||||
|
||||
def setup_postgres_trigrams():
|
||||
op.execute("CREATE EXTENSION IF NOT EXISTS pg_trgm;")
|
||||
op.create_index(
|
||||
"ix_recipes_name_normalized_gin",
|
||||
table_name="recipes",
|
||||
columns=["name_normalized"],
|
||||
unique=False,
|
||||
postgresql_using="gin",
|
||||
postgresql_ops={
|
||||
"name_normalized": "gin_trgm_ops",
|
||||
},
|
||||
)
|
||||
op.create_index(
|
||||
"ix_recipes_description_normalized_gin",
|
||||
table_name="recipes",
|
||||
columns=["description_normalized"],
|
||||
unique=False,
|
||||
postgresql_using="gin",
|
||||
postgresql_ops={
|
||||
"description_normalized": "gin_trgm_ops",
|
||||
},
|
||||
)
|
||||
op.create_index(
|
||||
"ix_recipes_ingredients_note_normalized_gin",
|
||||
table_name="recipes_ingredients",
|
||||
columns=["note_normalized"],
|
||||
unique=False,
|
||||
postgresql_using="gin",
|
||||
postgresql_ops={
|
||||
"note_normalized": "gin_trgm_ops",
|
||||
},
|
||||
)
|
||||
op.create_index(
|
||||
"ix_recipes_ingredients_original_text_normalized_gin",
|
||||
table_name="recipes_ingredients",
|
||||
columns=["original_text_normalized"],
|
||||
unique=False,
|
||||
postgresql_using="gin",
|
||||
postgresql_ops={
|
||||
"original_text_normalized": "gin_trgm_ops",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def remove_postgres_trigrams():
|
||||
op.execute("DROP EXTENSION IF EXISTS pg_trgm;")
|
||||
op.drop_index("ix_recipes_name_normalized_gin", table_name="recipe")
|
||||
op.drop_index("ix_recipes_description_normalized_gin", table_name="recipe")
|
||||
op.drop_index("ix_recipes_ingredients_note_normalized_gin", table_name="recipes_ingredients")
|
||||
op.drop_index("ix_recipes_ingredients_original_text_normalized_gin", table_name="recipes_ingredients")
|
||||
|
||||
|
||||
def upgrade():
|
||||
if get_db_type() == "postgresql":
|
||||
setup_postgres_trigrams()
|
||||
else:
|
||||
pass
|
||||
|
||||
|
||||
def downgrade():
|
||||
if get_db_type() == "postgres":
|
||||
remove_postgres_trigrams()
|
||||
else:
|
||||
pass
|
||||
@@ -0,0 +1,62 @@
|
||||
"""added group slug
|
||||
|
||||
Revision ID: 04ac51cbe9a4
|
||||
Revises: b3dbb554ba53
|
||||
Create Date: 2023-08-06 21:00:34.582905
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
from slugify import slugify
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from alembic import op
|
||||
from mealie.db.models.group.group import Group
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "04ac51cbe9a4"
|
||||
down_revision = "b3dbb554ba53"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def populate_group_slugs(session: Session):
|
||||
groups: list[Group] = session.query(Group).all()
|
||||
seen_slugs: set[str] = set()
|
||||
for group in groups:
|
||||
original_name = group.name
|
||||
new_name = original_name
|
||||
attempts = 0
|
||||
while True:
|
||||
slug = slugify(new_name)
|
||||
if slug not in seen_slugs:
|
||||
break
|
||||
|
||||
attempts += 1
|
||||
new_name = f"{original_name} ({attempts})"
|
||||
|
||||
seen_slugs.add(slug)
|
||||
session.execute(
|
||||
sa.text(f"UPDATE {Group.__tablename__} SET name=:name, slug=:slug WHERE id=:id").bindparams(
|
||||
name=new_name, slug=slug, id=group.id
|
||||
)
|
||||
)
|
||||
|
||||
session.commit()
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column("groups", sa.Column("slug", sa.String(), nullable=True))
|
||||
op.create_index(op.f("ix_groups_slug"), "groups", ["slug"], unique=True)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
session = Session(bind=op.get_bind())
|
||||
populate_group_slugs(session)
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index(op.f("ix_groups_slug"), table_name="groups")
|
||||
op.drop_column("groups", "slug")
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,29 @@
|
||||
"""added recipe note to shopping list recipe ref
|
||||
|
||||
Revision ID: 1825b5225403
|
||||
Revises: 04ac51cbe9a4
|
||||
Create Date: 2023-08-14 19:30:49.103185
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "1825b5225403"
|
||||
down_revision = "04ac51cbe9a4"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column("shopping_list_item_recipe_reference", sa.Column("recipe_note", sa.String(), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column("shopping_list_item_recipe_reference", "recipe_note")
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,33 @@
|
||||
"""remove tool name and slug unique contraints
|
||||
|
||||
Revision ID: bcfdad6b7355
|
||||
Revises: 1825b5225403
|
||||
Create Date: 2023-08-15 16:25:07.058929
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "bcfdad6b7355"
|
||||
down_revision = "1825b5225403"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index("ix_tools_name", table_name="tools")
|
||||
op.create_index(op.f("ix_tools_name"), "tools", ["name"], unique=False)
|
||||
op.drop_index("ix_tools_slug", table_name="tools")
|
||||
op.create_index(op.f("ix_tools_slug"), "tools", ["slug"], unique=False)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index(op.f("ix_tools_slug"), table_name="tools")
|
||||
op.create_index("ix_tools_slug", "tools", ["slug"], unique=True)
|
||||
op.drop_index(op.f("ix_tools_name"), table_name="tools")
|
||||
op.create_index("ix_tools_name", "tools", ["name"], unique=True)
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,90 @@
|
||||
"""added normalized unit and food names
|
||||
|
||||
Revision ID: 0341b154f79a
|
||||
Revises: bcfdad6b7355
|
||||
Create Date: 2023-09-01 14:55:42.166766
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import orm, select
|
||||
|
||||
from alembic import op
|
||||
from mealie.db.models.recipe.ingredient import IngredientFoodModel, IngredientUnitModel
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "0341b154f79a"
|
||||
down_revision = "bcfdad6b7355"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def populate_normalized_fields():
|
||||
bind = op.get_bind()
|
||||
session = orm.Session(bind=bind)
|
||||
|
||||
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:
|
||||
session.execute(
|
||||
sa.text(
|
||||
f"UPDATE {IngredientUnitModel.__tablename__} SET name_normalized=:name_normalized WHERE id=:id"
|
||||
).bindparams(name_normalized=IngredientUnitModel.normalize(unit.name), id=unit.id)
|
||||
)
|
||||
|
||||
if unit.abbreviation is not None:
|
||||
session.execute(
|
||||
sa.text(
|
||||
f"UPDATE {IngredientUnitModel.__tablename__} SET abbreviation_normalized=:abbreviation_normalized WHERE id=:id"
|
||||
).bindparams(abbreviation_normalized=IngredientUnitModel.normalize(unit.abbreviation), id=unit.id)
|
||||
)
|
||||
|
||||
foods = (
|
||||
session.execute(select(IngredientFoodModel).options(orm.load_only(IngredientFoodModel.name))).scalars().all()
|
||||
)
|
||||
for food in foods:
|
||||
if food.name is not None:
|
||||
session.execute(
|
||||
sa.text(
|
||||
f"UPDATE {IngredientFoodModel.__tablename__} SET name_normalized=:name_normalized WHERE id=:id"
|
||||
).bindparams(name_normalized=IngredientFoodModel.normalize(food.name), id=food.id)
|
||||
)
|
||||
|
||||
session.commit()
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column("ingredient_foods", sa.Column("name_normalized", sa.String(), nullable=True))
|
||||
op.create_index(op.f("ix_ingredient_foods_name_normalized"), "ingredient_foods", ["name_normalized"], unique=False)
|
||||
op.add_column("ingredient_units", sa.Column("name_normalized", sa.String(), nullable=True))
|
||||
op.add_column("ingredient_units", sa.Column("abbreviation_normalized", sa.String(), nullable=True))
|
||||
op.create_index(
|
||||
op.f("ix_ingredient_units_abbreviation_normalized"),
|
||||
"ingredient_units",
|
||||
["abbreviation_normalized"],
|
||||
unique=False,
|
||||
)
|
||||
op.create_index(op.f("ix_ingredient_units_name_normalized"), "ingredient_units", ["name_normalized"], unique=False)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
populate_normalized_fields()
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index(op.f("ix_ingredient_units_name_normalized"), table_name="ingredient_units")
|
||||
op.drop_index(op.f("ix_ingredient_units_abbreviation_normalized"), table_name="ingredient_units")
|
||||
op.drop_column("ingredient_units", "abbreviation_normalized")
|
||||
op.drop_column("ingredient_units", "name_normalized")
|
||||
op.drop_index(op.f("ix_ingredient_foods_name_normalized"), table_name="ingredient_foods")
|
||||
op.drop_column("ingredient_foods", "name_normalized")
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,306 @@
|
||||
"""added unique constraints
|
||||
|
||||
Revision ID: dded3119c1fe
|
||||
Revises: 0341b154f79a
|
||||
Create Date: 2023-10-04 14:29:26.688065
|
||||
|
||||
"""
|
||||
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
import sqlalchemy as sa
|
||||
from pydantic import UUID4
|
||||
from sqlalchemy import orm
|
||||
from sqlalchemy.orm import Session, load_only
|
||||
|
||||
from alembic import op
|
||||
from mealie.db.models._model_utils.guid import GUID
|
||||
from mealie.db.models.labels import MultiPurposeLabel
|
||||
from mealie.db.models.recipe.ingredient import IngredientFoodModel, IngredientUnitModel, RecipeIngredientModel
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "dded3119c1fe"
|
||||
down_revision = "0341b154f79a"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
# Intermediate table definitions
|
||||
class SqlAlchemyBase(orm.DeclarativeBase):
|
||||
pass
|
||||
|
||||
|
||||
class ShoppingList(SqlAlchemyBase):
|
||||
__tablename__ = "shopping_lists"
|
||||
|
||||
id: orm.Mapped[GUID] = orm.mapped_column(GUID, primary_key=True, default=GUID.generate)
|
||||
group_id: orm.Mapped[GUID] = orm.mapped_column(GUID, sa.ForeignKey("groups.id"), nullable=False, index=True)
|
||||
|
||||
|
||||
class ShoppingListItem(SqlAlchemyBase):
|
||||
__tablename__ = "shopping_list_items"
|
||||
|
||||
id: orm.Mapped[GUID] = orm.mapped_column(GUID, primary_key=True, default=GUID.generate)
|
||||
food_id: orm.Mapped[GUID] = orm.mapped_column(GUID, sa.ForeignKey("ingredient_foods.id"))
|
||||
unit_id: orm.Mapped[GUID] = orm.mapped_column(GUID, sa.ForeignKey("ingredient_units.id"))
|
||||
label_id: orm.Mapped[GUID] = orm.mapped_column(GUID, sa.ForeignKey("multi_purpose_labels.id"))
|
||||
|
||||
|
||||
@dataclass
|
||||
class TableMeta:
|
||||
tablename: str
|
||||
pk_1: str
|
||||
pk_2: str
|
||||
|
||||
@classmethod
|
||||
def composite_pk(self, pk_1_val: Any, pk_2_val: Any) -> str:
|
||||
return "$$".join([pk_1_val, pk_2_val])
|
||||
|
||||
|
||||
def _is_postgres():
|
||||
return op.get_context().dialect.name == "postgresql"
|
||||
|
||||
|
||||
def _get_duplicates(session: Session, model: orm.DeclarativeBase) -> defaultdict[str, list]:
|
||||
duplicate_map: defaultdict[str, list] = defaultdict(list)
|
||||
|
||||
query = session.execute(sa.text(f"SELECT id, group_id, name FROM {model.__tablename__}"))
|
||||
for row in query.all():
|
||||
id, group_id, name = row
|
||||
key = f"{group_id}$${name}"
|
||||
duplicate_map[key].append(id)
|
||||
|
||||
return duplicate_map
|
||||
|
||||
|
||||
def _resolve_duplicate_food(
|
||||
session: Session,
|
||||
keep_food_id: UUID4,
|
||||
dupe_food_id: UUID4,
|
||||
):
|
||||
for shopping_list_item in (
|
||||
session.query(ShoppingListItem)
|
||||
.options(load_only(ShoppingListItem.id, ShoppingListItem.food_id))
|
||||
.filter_by(food_id=dupe_food_id)
|
||||
.all()
|
||||
):
|
||||
shopping_list_item.food_id = keep_food_id
|
||||
|
||||
for recipe_ingredient in (
|
||||
session.query(RecipeIngredientModel)
|
||||
.options(load_only(RecipeIngredientModel.id, RecipeIngredientModel.food_id))
|
||||
.filter_by(food_id=dupe_food_id)
|
||||
.all()
|
||||
):
|
||||
recipe_ingredient.food_id = keep_food_id
|
||||
|
||||
session.commit()
|
||||
session.execute(
|
||||
sa.text(f"DELETE FROM {IngredientFoodModel.__tablename__} WHERE id=:id").bindparams(id=dupe_food_id)
|
||||
)
|
||||
session.commit()
|
||||
|
||||
|
||||
def _resolve_duplicate_unit(
|
||||
session: Session,
|
||||
keep_unit_id: UUID4,
|
||||
dupe_unit_id: UUID4,
|
||||
):
|
||||
for shopping_list_item in (
|
||||
session.query(ShoppingListItem)
|
||||
.options(load_only(ShoppingListItem.id, ShoppingListItem.unit_id))
|
||||
.filter_by(unit_id=dupe_unit_id)
|
||||
.all()
|
||||
):
|
||||
shopping_list_item.unit_id = keep_unit_id
|
||||
|
||||
for recipe_ingredient in (
|
||||
session.query(RecipeIngredientModel)
|
||||
.options(load_only(RecipeIngredientModel.id, RecipeIngredientModel.unit_id))
|
||||
.filter_by(unit_id=dupe_unit_id)
|
||||
.all()
|
||||
):
|
||||
recipe_ingredient.unit_id = keep_unit_id
|
||||
|
||||
session.commit()
|
||||
session.execute(
|
||||
sa.text(f"DELETE FROM {IngredientUnitModel.__tablename__} WHERE id=:id").bindparams(id=dupe_unit_id)
|
||||
)
|
||||
session.commit()
|
||||
|
||||
|
||||
def _resolve_duplicate_label(
|
||||
session: Session,
|
||||
keep_label_id: UUID4,
|
||||
dupe_label_id: UUID4,
|
||||
):
|
||||
for shopping_list_item in (
|
||||
session.query(ShoppingListItem)
|
||||
.options(load_only(ShoppingListItem.id, ShoppingListItem.label_id))
|
||||
.filter_by(label_id=dupe_label_id)
|
||||
.all()
|
||||
):
|
||||
shopping_list_item.label_id = keep_label_id
|
||||
|
||||
for ingredient_food in (
|
||||
session.query(IngredientFoodModel)
|
||||
.options(load_only(IngredientFoodModel.id, IngredientFoodModel.label_id))
|
||||
.filter_by(label_id=dupe_label_id)
|
||||
.all()
|
||||
):
|
||||
ingredient_food.label_id = keep_label_id
|
||||
|
||||
session.commit()
|
||||
session.execute(sa.text(f"DELETE FROM {MultiPurposeLabel.__tablename__} WHERE id=:id").bindparams(id=dupe_label_id))
|
||||
session.commit()
|
||||
|
||||
|
||||
def _resolve_duplicate_foods_units_labels(session: Session):
|
||||
for model, resolve_func in [
|
||||
(IngredientFoodModel, _resolve_duplicate_food),
|
||||
(IngredientUnitModel, _resolve_duplicate_unit),
|
||||
(MultiPurposeLabel, _resolve_duplicate_label),
|
||||
]:
|
||||
duplicate_map = _get_duplicates(session, model)
|
||||
for ids in duplicate_map.values():
|
||||
if len(ids) < 2:
|
||||
continue
|
||||
|
||||
keep_id = ids[0]
|
||||
for dupe_id in ids[1:]:
|
||||
resolve_func(session, keep_id, dupe_id)
|
||||
|
||||
|
||||
def _remove_duplicates_from_m2m_table(session: Session, table_meta: TableMeta):
|
||||
if _is_postgres():
|
||||
default_pk = "CTID"
|
||||
else:
|
||||
default_pk = "ROWID"
|
||||
|
||||
# some of these tables are missing defined unique pks, so we have to rely on the database default pk
|
||||
query = sa.text(
|
||||
f"""
|
||||
DELETE FROM {table_meta.tablename}
|
||||
WHERE EXISTS (
|
||||
SELECT 1 FROM {table_meta.tablename} t2
|
||||
WHERE {table_meta.tablename}.{table_meta.pk_1} = t2.{table_meta.pk_1}
|
||||
AND {table_meta.tablename}.{table_meta.pk_2} = t2.{table_meta.pk_2}
|
||||
AND {table_meta.tablename}.{default_pk} > t2.{default_pk}
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
session.execute(query)
|
||||
session.commit()
|
||||
|
||||
|
||||
def _remove_duplicates_from_m2m_tables(session: Session, table_metas: list[TableMeta]):
|
||||
for table_meta in table_metas:
|
||||
_remove_duplicates_from_m2m_table(session, table_meta)
|
||||
|
||||
|
||||
def upgrade():
|
||||
bind = op.get_bind()
|
||||
session = Session(bind=bind)
|
||||
|
||||
_resolve_duplicate_foods_units_labels(session)
|
||||
_remove_duplicates_from_m2m_tables(
|
||||
session,
|
||||
[
|
||||
TableMeta("cookbooks_to_categories", "cookbook_id", "category_id"),
|
||||
TableMeta("cookbooks_to_tags", "cookbook_id", "tag_id"),
|
||||
TableMeta("cookbooks_to_tools", "cookbook_id", "tool_id"),
|
||||
TableMeta("group_to_categories", "group_id", "category_id"),
|
||||
TableMeta("plan_rules_to_categories", "group_plan_rule_id", "category_id"),
|
||||
TableMeta("plan_rules_to_tags", "plan_rule_id", "tag_id"),
|
||||
TableMeta("recipes_to_categories", "recipe_id", "category_id"),
|
||||
TableMeta("recipes_to_tags", "recipe_id", "tag_id"),
|
||||
TableMeta("recipes_to_tools", "recipe_id", "tool_id"),
|
||||
TableMeta("users_to_favorites", "user_id", "recipe_id"),
|
||||
TableMeta("shopping_lists_multi_purpose_labels", "shopping_list_id", "label_id"),
|
||||
],
|
||||
)
|
||||
|
||||
session.commit()
|
||||
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
# we use batch_alter_table here because otherwise this fails on sqlite
|
||||
# M2M
|
||||
with op.batch_alter_table("cookbooks_to_categories") as batch_op:
|
||||
batch_op.create_unique_constraint("cookbook_id_category_id_key", ["cookbook_id", "category_id"])
|
||||
|
||||
with op.batch_alter_table("cookbooks_to_tags") as batch_op:
|
||||
batch_op.create_unique_constraint("cookbook_id_tag_id_key", ["cookbook_id", "tag_id"])
|
||||
|
||||
with op.batch_alter_table("cookbooks_to_tools") as batch_op:
|
||||
batch_op.create_unique_constraint("cookbook_id_tool_id_key", ["cookbook_id", "tool_id"])
|
||||
|
||||
with op.batch_alter_table("group_to_categories") as batch_op:
|
||||
batch_op.create_unique_constraint("group_id_category_id_key", ["group_id", "category_id"])
|
||||
|
||||
with op.batch_alter_table("plan_rules_to_categories") as batch_op:
|
||||
batch_op.create_unique_constraint("group_plan_rule_id_category_id_key", ["group_plan_rule_id", "category_id"])
|
||||
|
||||
with op.batch_alter_table("plan_rules_to_tags") as batch_op:
|
||||
batch_op.create_unique_constraint("plan_rule_id_tag_id_key", ["plan_rule_id", "tag_id"])
|
||||
|
||||
with op.batch_alter_table("recipes_to_categories") as batch_op:
|
||||
batch_op.create_unique_constraint("recipe_id_category_id_key", ["recipe_id", "category_id"])
|
||||
|
||||
with op.batch_alter_table("recipes_to_tags") as batch_op:
|
||||
batch_op.create_unique_constraint("recipe_id_tag_id_key", ["recipe_id", "tag_id"])
|
||||
|
||||
with op.batch_alter_table("recipes_to_tools") as batch_op:
|
||||
batch_op.create_unique_constraint("recipe_id_tool_id_key", ["recipe_id", "tool_id"])
|
||||
|
||||
with op.batch_alter_table("users_to_favorites") as batch_op:
|
||||
batch_op.create_unique_constraint("user_id_recipe_id_key", ["user_id", "recipe_id"])
|
||||
|
||||
with op.batch_alter_table("shopping_lists_multi_purpose_labels") as batch_op:
|
||||
batch_op.create_unique_constraint("shopping_list_id_label_id_key", ["shopping_list_id", "label_id"])
|
||||
|
||||
# Foods/Units/Labels
|
||||
with op.batch_alter_table("ingredient_foods") as batch_op:
|
||||
batch_op.create_unique_constraint("ingredient_foods_name_group_id_key", ["name", "group_id"])
|
||||
|
||||
with op.batch_alter_table("ingredient_units") as batch_op:
|
||||
batch_op.create_unique_constraint("ingredient_units_name_group_id_key", ["name", "group_id"])
|
||||
|
||||
with op.batch_alter_table("multi_purpose_labels") as batch_op:
|
||||
batch_op.create_unique_constraint("multi_purpose_labels_name_group_id_key", ["name", "group_id"])
|
||||
|
||||
op.create_index(
|
||||
op.f("ix_shopping_lists_multi_purpose_labels_created_at"),
|
||||
"shopping_lists_multi_purpose_labels",
|
||||
["created_at"],
|
||||
unique=False,
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
|
||||
# M2M
|
||||
op.drop_constraint("user_id_recipe_id_key", "users_to_favorites", type_="unique")
|
||||
op.drop_index(
|
||||
op.f("ix_shopping_lists_multi_purpose_labels_created_at"), table_name="shopping_lists_multi_purpose_labels"
|
||||
)
|
||||
op.drop_constraint("recipe_id_tool_id_key", "recipes_to_tools", type_="unique")
|
||||
op.drop_constraint("recipe_id_tag_id_key", "recipes_to_tags", type_="unique")
|
||||
op.drop_constraint("recipe_id_category_id_key", "recipes_to_categories", type_="unique")
|
||||
op.drop_constraint("plan_rule_id_tag_id_key", "plan_rules_to_tags", type_="unique")
|
||||
op.drop_constraint("group_plan_rule_id_category_id_key", "plan_rules_to_categories", type_="unique")
|
||||
op.drop_constraint("group_id_category_id_key", "group_to_categories", type_="unique")
|
||||
op.drop_constraint("cookbook_id_tool_id_key", "cookbooks_to_tools", type_="unique")
|
||||
op.drop_constraint("cookbook_id_tag_id_key", "cookbooks_to_tags", type_="unique")
|
||||
op.drop_constraint("cookbook_id_category_id_key", "cookbooks_to_categories", type_="unique")
|
||||
op.drop_constraint("shopping_list_id_label_id_key", "shopping_lists_multi_purpose_labels", type_="unique")
|
||||
|
||||
# Foods/Units/Labels
|
||||
op.drop_constraint("multi_purpose_labels_name_group_id_key", "multi_purpose_labels", type_="unique")
|
||||
op.drop_constraint("ingredient_units_name_group_id_key", "ingredient_units", type_="unique")
|
||||
op.drop_constraint("ingredient_foods_name_group_id_key", "ingredient_foods", type_="unique")
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,107 @@
|
||||
"""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: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = 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 ###
|
||||
@@ -0,0 +1,101 @@
|
||||
"""added user to shopping list
|
||||
|
||||
Revision ID: 2298bb460ffd
|
||||
Revises: ba1e4a6cfe99
|
||||
Create Date: 2024-02-23 16:15:07.115641
|
||||
|
||||
"""
|
||||
|
||||
from uuid import UUID
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
from sqlalchemy import orm
|
||||
|
||||
import mealie.db.migration_types
|
||||
from mealie.core.root_logger import get_logger
|
||||
|
||||
logger = get_logger()
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "2298bb460ffd"
|
||||
down_revision = "ba1e4a6cfe99"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def is_postgres():
|
||||
return op.get_context().dialect.name == "postgresql"
|
||||
|
||||
|
||||
def find_user_id_for_group(group_id: UUID):
|
||||
bind = op.get_bind()
|
||||
session = orm.Session(bind=bind)
|
||||
|
||||
if is_postgres():
|
||||
stmt = "SELECT id FROM users WHERE group_id=:group_id AND admin = TRUE LIMIT 1"
|
||||
else:
|
||||
stmt = "SELECT id FROM users WHERE group_id=:group_id AND admin = 1 LIMIT 1"
|
||||
|
||||
with session:
|
||||
try:
|
||||
# try to find an admin user
|
||||
return session.execute(sa.text(stmt).bindparams(group_id=group_id)).scalar_one()
|
||||
except orm.exc.NoResultFound:
|
||||
pass
|
||||
|
||||
try:
|
||||
# fallback to any user
|
||||
return session.execute(
|
||||
sa.text("SELECT id FROM users WHERE group_id=:group_id LIMIT 1").bindparams(group_id=group_id)
|
||||
).scalar_one()
|
||||
except orm.exc.NoResultFound:
|
||||
pass
|
||||
|
||||
# no user could be found
|
||||
return None
|
||||
|
||||
|
||||
def populate_shopping_list_users():
|
||||
bind = op.get_bind()
|
||||
session = orm.Session(bind=bind)
|
||||
|
||||
with session:
|
||||
list_ids_and_group_ids = session.execute(sa.text("SELECT id, group_id FROM shopping_lists")).all()
|
||||
for list_id, group_id in list_ids_and_group_ids:
|
||||
user_id = find_user_id_for_group(group_id)
|
||||
if user_id:
|
||||
session.execute(
|
||||
sa.text("UPDATE shopping_lists SET user_id=:user_id WHERE id=:id").bindparams(
|
||||
user_id=user_id, id=list_id
|
||||
)
|
||||
)
|
||||
else:
|
||||
logger.warning(
|
||||
f"No user found for shopping list {list_id} with group {group_id}; deleting shopping list"
|
||||
)
|
||||
session.execute(sa.text("DELETE FROM shopping_lists WHERE id=:id").bindparams(id=list_id))
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table("shopping_lists") as batch_op:
|
||||
# allow nulls during migration
|
||||
batch_op.add_column(sa.Column("user_id", mealie.db.migration_types.GUID(), nullable=True))
|
||||
batch_op.create_index(op.f("ix_shopping_lists_user_id"), ["user_id"], unique=False)
|
||||
batch_op.create_foreign_key("fk_user_shopping_lists", "users", ["user_id"], ["id"])
|
||||
# ### end Alembic commands ###
|
||||
|
||||
populate_shopping_list_users()
|
||||
|
||||
# forbid nulls after migration
|
||||
with op.batch_alter_table("shopping_lists") as batch_op:
|
||||
batch_op.alter_column("user_id", nullable=False)
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, "shopping_lists", type_="foreignkey")
|
||||
op.drop_index(op.f("ix_shopping_lists_user_id"), table_name="shopping_lists")
|
||||
op.drop_column("shopping_lists", "user_id")
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,28 @@
|
||||
"""add OIDC auth method
|
||||
|
||||
Revision ID: 09aba125b57a
|
||||
Revises: 2298bb460ffd
|
||||
Create Date: 2024-03-10 05:08:32.397027
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "09aba125b57a"
|
||||
down_revision = "2298bb460ffd"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def is_postgres():
|
||||
return op.get_context().dialect.name == "postgresql"
|
||||
|
||||
|
||||
def upgrade():
|
||||
if is_postgres():
|
||||
op.execute("ALTER TYPE authmethod ADD VALUE 'OIDC'")
|
||||
|
||||
|
||||
def downgrade():
|
||||
pass
|
||||
@@ -0,0 +1,227 @@
|
||||
"""migrate favorites and ratings to user_ratings
|
||||
|
||||
Revision ID: d7c6efd2de42
|
||||
Revises: 09aba125b57a
|
||||
Create Date: 2024-03-18 02:28:15.896959
|
||||
|
||||
"""
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from textwrap import dedent
|
||||
from typing import Any
|
||||
from uuid import uuid4
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
from sqlalchemy import orm
|
||||
|
||||
import mealie.db.migration_types
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "d7c6efd2de42"
|
||||
down_revision = "09aba125b57a"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def is_postgres():
|
||||
return op.get_context().dialect.name == "postgresql"
|
||||
|
||||
|
||||
def new_user_rating(user_id: Any, recipe_id: Any, rating: float | None = None, is_favorite: bool = False):
|
||||
if is_postgres():
|
||||
id = str(uuid4())
|
||||
else:
|
||||
id = "%.32x" % uuid4().int # noqa: UP031
|
||||
|
||||
now = datetime.now(timezone.utc).isoformat()
|
||||
return {
|
||||
"id": id,
|
||||
"user_id": user_id,
|
||||
"recipe_id": recipe_id,
|
||||
"rating": rating,
|
||||
"is_favorite": is_favorite,
|
||||
"created_at": now,
|
||||
"update_at": now,
|
||||
}
|
||||
|
||||
|
||||
def migrate_user_favorites_to_user_ratings():
|
||||
bind = op.get_bind()
|
||||
session = orm.Session(bind=bind)
|
||||
|
||||
with session:
|
||||
user_ids_and_recipe_ids = session.execute(sa.text("SELECT user_id, recipe_id FROM users_to_favorites")).all()
|
||||
rows = [
|
||||
new_user_rating(user_id, recipe_id, is_favorite=True)
|
||||
for user_id, recipe_id in user_ids_and_recipe_ids
|
||||
if user_id and recipe_id
|
||||
]
|
||||
|
||||
if is_postgres():
|
||||
query = dedent(
|
||||
"""
|
||||
INSERT INTO users_to_recipes (id, user_id, recipe_id, rating, is_favorite, created_at, update_at)
|
||||
VALUES (:id, :user_id, :recipe_id, :rating, :is_favorite, :created_at, :update_at)
|
||||
ON CONFLICT DO NOTHING
|
||||
"""
|
||||
)
|
||||
else:
|
||||
query = dedent(
|
||||
"""
|
||||
INSERT OR IGNORE INTO users_to_recipes
|
||||
(id, user_id, recipe_id, rating, is_favorite, created_at, update_at)
|
||||
VALUES (:id, :user_id, :recipe_id, :rating, :is_favorite, :created_at, :update_at)
|
||||
"""
|
||||
)
|
||||
|
||||
for row in rows:
|
||||
session.execute(sa.text(query), row)
|
||||
|
||||
|
||||
def migrate_group_to_user_ratings(group_id: Any):
|
||||
bind = op.get_bind()
|
||||
session = orm.Session(bind=bind)
|
||||
|
||||
with session:
|
||||
user_ids = (
|
||||
session.execute(sa.text("SELECT id FROM users WHERE group_id=:group_id").bindparams(group_id=group_id))
|
||||
.scalars()
|
||||
.all()
|
||||
)
|
||||
|
||||
recipe_ids_ratings = session.execute(
|
||||
sa.text(
|
||||
"SELECT id, rating FROM recipes WHERE group_id=:group_id AND rating > 0 AND rating IS NOT NULL"
|
||||
).bindparams(group_id=group_id)
|
||||
).all()
|
||||
|
||||
# Convert recipe ratings to user ratings. Since we don't know who
|
||||
# rated the recipe initially, we copy the rating to all users.
|
||||
rows: list[dict] = []
|
||||
for recipe_id, rating in recipe_ids_ratings:
|
||||
for user_id in user_ids:
|
||||
rows.append(new_user_rating(user_id, recipe_id, rating, is_favorite=False))
|
||||
|
||||
if is_postgres():
|
||||
insert_query = dedent(
|
||||
"""
|
||||
INSERT INTO users_to_recipes (id, user_id, recipe_id, rating, is_favorite, created_at, update_at)
|
||||
VALUES (:id, :user_id, :recipe_id, :rating, :is_favorite, :created_at, :update_at)
|
||||
ON CONFLICT (user_id, recipe_id) DO NOTHING;
|
||||
"""
|
||||
)
|
||||
else:
|
||||
insert_query = dedent(
|
||||
"""
|
||||
INSERT OR IGNORE INTO users_to_recipes
|
||||
(id, user_id, recipe_id, rating, is_favorite, created_at, update_at)
|
||||
VALUES (:id, :user_id, :recipe_id, :rating, :is_favorite, :created_at, :update_at);
|
||||
"""
|
||||
)
|
||||
|
||||
update_query = dedent(
|
||||
"""
|
||||
UPDATE users_to_recipes
|
||||
SET rating = :rating, update_at = :update_at
|
||||
WHERE user_id = :user_id AND recipe_id = :recipe_id;
|
||||
"""
|
||||
)
|
||||
|
||||
# Create new user ratings with is_favorite set to False
|
||||
for row in rows:
|
||||
session.execute(sa.text(insert_query), row)
|
||||
|
||||
# Update existing user ratings with the correct rating
|
||||
for row in rows:
|
||||
session.execute(sa.text(update_query), row)
|
||||
|
||||
|
||||
def migrate_to_user_ratings():
|
||||
migrate_user_favorites_to_user_ratings()
|
||||
|
||||
bind = op.get_bind()
|
||||
session = orm.Session(bind=bind)
|
||||
|
||||
with session:
|
||||
group_ids = session.execute(sa.text("SELECT id FROM groups")).scalars().all()
|
||||
|
||||
for group_id in group_ids:
|
||||
migrate_group_to_user_ratings(group_id)
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table(
|
||||
"users_to_recipes",
|
||||
sa.Column("user_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("recipe_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("rating", sa.Float(), nullable=True),
|
||||
sa.Column("is_favorite", sa.Boolean(), nullable=False),
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["recipe_id"],
|
||||
["recipes.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["user_id"],
|
||||
["users.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("user_id", "recipe_id", "id"),
|
||||
sa.UniqueConstraint("user_id", "recipe_id", name="user_id_recipe_id_rating_key"),
|
||||
)
|
||||
op.create_index(op.f("ix_users_to_recipes_created_at"), "users_to_recipes", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_users_to_recipes_is_favorite"), "users_to_recipes", ["is_favorite"], unique=False)
|
||||
op.create_index(op.f("ix_users_to_recipes_rating"), "users_to_recipes", ["rating"], unique=False)
|
||||
op.create_index(op.f("ix_users_to_recipes_recipe_id"), "users_to_recipes", ["recipe_id"], unique=False)
|
||||
op.create_index(op.f("ix_users_to_recipes_user_id"), "users_to_recipes", ["user_id"], unique=False)
|
||||
|
||||
migrate_to_user_ratings()
|
||||
|
||||
if is_postgres():
|
||||
op.drop_index("ix_users_to_favorites_recipe_id", table_name="users_to_favorites")
|
||||
op.drop_index("ix_users_to_favorites_user_id", table_name="users_to_favorites")
|
||||
op.alter_column("recipes", "rating", existing_type=sa.INTEGER(), type_=sa.Float(), existing_nullable=True)
|
||||
else:
|
||||
op.execute("DROP INDEX IF EXISTS ix_users_to_favorites_recipe_id")
|
||||
op.execute("DROP INDEX IF EXISTS ix_users_to_favorites_user_id")
|
||||
with op.batch_alter_table("recipes") as batch_op:
|
||||
batch_op.alter_column("rating", existing_type=sa.INTEGER(), type_=sa.Float(), existing_nullable=True)
|
||||
|
||||
op.drop_table("users_to_favorites")
|
||||
op.create_index(op.f("ix_recipes_rating"), "recipes", ["rating"], unique=False)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column(
|
||||
"recipes_ingredients", "quantity", existing_type=sa.Float(), type_=sa.INTEGER(), existing_nullable=True
|
||||
)
|
||||
op.drop_index(op.f("ix_recipes_rating"), table_name="recipes")
|
||||
op.alter_column("recipes", "rating", existing_type=sa.Float(), type_=sa.INTEGER(), existing_nullable=True)
|
||||
op.create_table(
|
||||
"users_to_favorites",
|
||||
sa.Column("user_id", sa.CHAR(length=32), nullable=True),
|
||||
sa.Column("recipe_id", sa.CHAR(length=32), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["recipe_id"],
|
||||
["recipes.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["user_id"],
|
||||
["users.id"],
|
||||
),
|
||||
sa.UniqueConstraint("user_id", "recipe_id", name="user_id_recipe_id_key"),
|
||||
)
|
||||
op.create_index("ix_users_to_favorites_user_id", "users_to_favorites", ["user_id"], unique=False)
|
||||
op.create_index("ix_users_to_favorites_recipe_id", "users_to_favorites", ["recipe_id"], unique=False)
|
||||
op.drop_index(op.f("ix_users_to_recipes_user_id"), table_name="users_to_recipes")
|
||||
op.drop_index(op.f("ix_users_to_recipes_recipe_id"), table_name="users_to_recipes")
|
||||
op.drop_index(op.f("ix_users_to_recipes_rating"), table_name="users_to_recipes")
|
||||
op.drop_index(op.f("ix_users_to_recipes_is_favorite"), table_name="users_to_recipes")
|
||||
op.drop_index(op.f("ix_users_to_recipes_created_at"), table_name="users_to_recipes")
|
||||
op.drop_table("users_to_recipes")
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,52 @@
|
||||
"""add group recipe actions
|
||||
|
||||
Revision ID: 7788478a0338
|
||||
Revises: d7c6efd2de42
|
||||
Create Date: 2024-04-07 01:05:20.816270
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
import mealie.db.migration_types
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "7788478a0338"
|
||||
down_revision = "d7c6efd2de42"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table(
|
||||
"recipe_actions",
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("action_type", sa.String(), nullable=False),
|
||||
sa.Column("title", sa.String(), nullable=False),
|
||||
sa.Column("url", sa.String(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_id"],
|
||||
["groups.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_recipe_actions_action_type"), "recipe_actions", ["action_type"], unique=False)
|
||||
op.create_index(op.f("ix_recipe_actions_created_at"), "recipe_actions", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_recipe_actions_group_id"), "recipe_actions", ["group_id"], unique=False)
|
||||
op.create_index(op.f("ix_recipe_actions_title"), "recipe_actions", ["title"], unique=False)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index(op.f("ix_recipe_actions_title"), table_name="recipe_actions")
|
||||
op.drop_index(op.f("ix_recipe_actions_group_id"), table_name="recipe_actions")
|
||||
op.drop_index(op.f("ix_recipe_actions_created_at"), table_name="recipe_actions")
|
||||
op.drop_index(op.f("ix_recipe_actions_action_type"), table_name="recipe_actions")
|
||||
op.drop_table("recipe_actions")
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,46 @@
|
||||
"""Add staple flag to foods
|
||||
|
||||
Revision ID: 32d69327997b
|
||||
Revises: 7788478a0338
|
||||
Create Date: 2024-06-22 10:17:03.323966
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
from sqlalchemy import orm
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "32d69327997b"
|
||||
down_revision = "7788478a0338"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def is_postgres():
|
||||
return op.get_context().dialect.name == "postgresql"
|
||||
|
||||
|
||||
def upgrade():
|
||||
with op.batch_alter_table("ingredient_foods") as batch_op:
|
||||
batch_op.add_column(sa.Column("on_hand", sa.Boolean(), nullable=True, default=False))
|
||||
|
||||
bind = op.get_bind()
|
||||
session = orm.Session(bind=bind)
|
||||
|
||||
with session:
|
||||
if is_postgres():
|
||||
stmt = "UPDATE ingredient_foods SET on_hand = FALSE;"
|
||||
else:
|
||||
stmt = "UPDATE ingredient_foods SET on_hand = 0;"
|
||||
|
||||
session.execute(sa.text(stmt))
|
||||
|
||||
# forbid nulls after migration
|
||||
with op.batch_alter_table("ingredient_foods") as batch_op:
|
||||
batch_op.alter_column("on_hand", nullable=False)
|
||||
|
||||
|
||||
def downgrade():
|
||||
with op.batch_alter_table("ingredient_foods") as batch_op:
|
||||
batch_op.drop_column("on_hand")
|
||||
@@ -0,0 +1,321 @@
|
||||
"""add households
|
||||
|
||||
Revision ID: feecc8ffb956
|
||||
Revises: 32d69327997b
|
||||
Create Date: 2024-07-12 16:16:29.973929
|
||||
|
||||
"""
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from textwrap import dedent
|
||||
from typing import Any
|
||||
from uuid import uuid4
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
from slugify import slugify
|
||||
from sqlalchemy import orm
|
||||
|
||||
import mealie.db.migration_types
|
||||
from mealie.core.config import get_app_settings
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "feecc8ffb956"
|
||||
down_revision = "32d69327997b"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
settings = get_app_settings()
|
||||
|
||||
|
||||
def is_postgres():
|
||||
return op.get_context().dialect.name == "postgresql"
|
||||
|
||||
|
||||
def generate_id() -> str:
|
||||
"""See GUID.convert_value_to_guid"""
|
||||
val = uuid4()
|
||||
if is_postgres():
|
||||
return str(val)
|
||||
else:
|
||||
return f"{val.int:032x}"
|
||||
|
||||
|
||||
def dedupe_cookbook_slugs():
|
||||
bind = op.get_bind()
|
||||
session = orm.Session(bind=bind)
|
||||
with session:
|
||||
sql = sa.text(
|
||||
dedent(
|
||||
"""
|
||||
SELECT slug, group_id, COUNT(*)
|
||||
FROM cookbooks
|
||||
GROUP BY slug, group_id
|
||||
HAVING COUNT(*) > 1
|
||||
"""
|
||||
)
|
||||
)
|
||||
rows = session.execute(sql).fetchall()
|
||||
|
||||
for slug, group_id, _ in rows:
|
||||
sql = sa.text(
|
||||
dedent(
|
||||
"""
|
||||
SELECT id
|
||||
FROM cookbooks
|
||||
WHERE slug = :slug AND group_id = :group_id
|
||||
ORDER BY id
|
||||
"""
|
||||
)
|
||||
)
|
||||
cookbook_ids = session.execute(sql, {"slug": slug, "group_id": group_id}).fetchall()
|
||||
|
||||
for i, (cookbook_id,) in enumerate(cookbook_ids):
|
||||
if i == 0:
|
||||
continue
|
||||
|
||||
sql = sa.text(
|
||||
dedent(
|
||||
"""
|
||||
UPDATE cookbooks
|
||||
SET slug = :slug || '-' || :i
|
||||
WHERE id = :id
|
||||
"""
|
||||
)
|
||||
)
|
||||
session.execute(sql, {"slug": slug, "i": i, "id": cookbook_id})
|
||||
|
||||
|
||||
def create_household(session: orm.Session, group_id: str) -> str:
|
||||
# create/insert household
|
||||
household_id = generate_id()
|
||||
timestamp = datetime.now(timezone.utc).isoformat()
|
||||
household_data = {
|
||||
"id": household_id,
|
||||
"name": settings.DEFAULT_HOUSEHOLD,
|
||||
"slug": slugify(settings.DEFAULT_HOUSEHOLD),
|
||||
"group_id": group_id,
|
||||
"created_at": timestamp,
|
||||
"update_at": timestamp,
|
||||
}
|
||||
columns = ", ".join(household_data.keys())
|
||||
placeholders = ", ".join(f":{key}" for key in household_data.keys())
|
||||
sql_statement = f"INSERT INTO households ({columns}) VALUES ({placeholders})"
|
||||
|
||||
session.execute(sa.text(sql_statement), household_data)
|
||||
|
||||
# fetch group preferences so we can copy them over to household preferences
|
||||
migrated_field_defaults = {
|
||||
"private_group": True, # this is renamed later
|
||||
"first_day_of_week": 0,
|
||||
"recipe_public": True,
|
||||
"recipe_show_nutrition": False,
|
||||
"recipe_show_assets": False,
|
||||
"recipe_landscape_view": False,
|
||||
"recipe_disable_comments": False,
|
||||
"recipe_disable_amount": True,
|
||||
}
|
||||
sql_statement = (
|
||||
f"SELECT {', '.join(migrated_field_defaults.keys())} FROM group_preferences WHERE group_id = :group_id"
|
||||
)
|
||||
group_preferences = session.execute(sa.text(sql_statement), {"group_id": group_id}).fetchone()
|
||||
|
||||
# build preferences data
|
||||
if group_preferences:
|
||||
preferences_data: dict[str, Any] = {}
|
||||
for i, (field, default_value) in enumerate(migrated_field_defaults.items()):
|
||||
value = group_preferences[i]
|
||||
preferences_data[field] = value if value is not None else default_value
|
||||
else:
|
||||
preferences_data = migrated_field_defaults
|
||||
|
||||
preferences_data["id"] = generate_id()
|
||||
preferences_data["household_id"] = household_id
|
||||
preferences_data["created_at"] = timestamp
|
||||
preferences_data["update_at"] = timestamp
|
||||
preferences_data["private_household"] = preferences_data.pop("private_group")
|
||||
|
||||
# insert preferences data
|
||||
columns = ", ".join(preferences_data.keys())
|
||||
placeholders = ", ".join(f":{key}" for key in preferences_data.keys())
|
||||
sql_statement = f"INSERT INTO household_preferences ({columns}) VALUES ({placeholders})"
|
||||
|
||||
session.execute(sa.text(sql_statement), preferences_data)
|
||||
|
||||
return household_id
|
||||
|
||||
|
||||
def create_households_for_groups() -> dict[str, str]:
|
||||
bind = op.get_bind()
|
||||
session = orm.Session(bind=bind)
|
||||
group_id_household_id_map: dict[str, str] = {}
|
||||
with session:
|
||||
rows = session.execute(sa.text("SELECT id FROM groups")).fetchall()
|
||||
for row in rows:
|
||||
group_id = row[0]
|
||||
group_id_household_id_map[group_id] = create_household(session, group_id)
|
||||
|
||||
return group_id_household_id_map
|
||||
|
||||
|
||||
def _do_assignment(session: orm.Session, table: str, group_id: str, household_id: str):
|
||||
sql = sa.text(
|
||||
dedent(
|
||||
f"""
|
||||
UPDATE {table}
|
||||
SET household_id = :household_id
|
||||
WHERE group_id = :group_id
|
||||
""",
|
||||
)
|
||||
)
|
||||
session.execute(sql, {"group_id": group_id, "household_id": household_id})
|
||||
|
||||
|
||||
def assign_households(group_id_household_id_map: dict[str, str]):
|
||||
tables = [
|
||||
"cookbooks",
|
||||
"group_events_notifiers",
|
||||
"group_meal_plan_rules",
|
||||
"invite_tokens",
|
||||
"recipe_actions",
|
||||
"users",
|
||||
"webhook_urls",
|
||||
]
|
||||
|
||||
bind = op.get_bind()
|
||||
session = orm.Session(bind=bind)
|
||||
with session:
|
||||
for table in tables:
|
||||
for group_id, household_id in group_id_household_id_map.items():
|
||||
_do_assignment(session, table, group_id, household_id)
|
||||
|
||||
|
||||
def populate_household_data():
|
||||
group_id_household_id_map = create_households_for_groups()
|
||||
assign_households(group_id_household_id_map)
|
||||
|
||||
|
||||
def upgrade():
|
||||
dedupe_cookbook_slugs()
|
||||
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table(
|
||||
"households",
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("name", sa.String(), nullable=False),
|
||||
sa.Column("slug", sa.String(), nullable=True),
|
||||
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_id"],
|
||||
["groups.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("group_id", "name", name="household_name_group_id_key"),
|
||||
sa.UniqueConstraint("group_id", "slug", name="household_slug_group_id_key"),
|
||||
)
|
||||
op.create_index(op.f("ix_households_created_at"), "households", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_households_group_id"), "households", ["group_id"], unique=False)
|
||||
op.create_index(op.f("ix_households_name"), "households", ["name"], unique=False)
|
||||
op.create_index(op.f("ix_households_slug"), "households", ["slug"], unique=False)
|
||||
op.create_table(
|
||||
"household_preferences",
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("household_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("private_household", sa.Boolean(), nullable=True),
|
||||
sa.Column("first_day_of_week", sa.Integer(), nullable=True),
|
||||
sa.Column("recipe_public", sa.Boolean(), nullable=True),
|
||||
sa.Column("recipe_show_nutrition", sa.Boolean(), nullable=True),
|
||||
sa.Column("recipe_show_assets", sa.Boolean(), nullable=True),
|
||||
sa.Column("recipe_landscape_view", sa.Boolean(), nullable=True),
|
||||
sa.Column("recipe_disable_comments", sa.Boolean(), nullable=True),
|
||||
sa.Column("recipe_disable_amount", sa.Boolean(), nullable=True),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["household_id"],
|
||||
["households.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_household_preferences_created_at"), "household_preferences", ["created_at"], unique=False)
|
||||
op.create_index(
|
||||
op.f("ix_household_preferences_household_id"), "household_preferences", ["household_id"], unique=False
|
||||
)
|
||||
|
||||
with op.batch_alter_table("cookbooks") as batch_op:
|
||||
batch_op.add_column(sa.Column("household_id", mealie.db.migration_types.GUID(), nullable=True))
|
||||
batch_op.create_index(op.f("ix_cookbooks_household_id"), ["household_id"], unique=False)
|
||||
batch_op.create_foreign_key("fk_cookbooks_household_id", "households", ["household_id"], ["id"])
|
||||
|
||||
# not directly related to households, but important for frontend routes
|
||||
batch_op.create_unique_constraint("cookbook_slug_group_id_key", ["slug", "group_id"])
|
||||
|
||||
with op.batch_alter_table("group_events_notifiers") as batch_op:
|
||||
batch_op.add_column(sa.Column("household_id", mealie.db.migration_types.GUID(), nullable=True))
|
||||
batch_op.create_index(op.f("ix_group_events_notifiers_household_id"), ["household_id"], unique=False)
|
||||
batch_op.create_foreign_key("fk_group_events_notifiers_household_id", "households", ["household_id"], ["id"])
|
||||
|
||||
with op.batch_alter_table("group_meal_plan_rules") as batch_op:
|
||||
batch_op.add_column(sa.Column("household_id", mealie.db.migration_types.GUID(), nullable=True))
|
||||
batch_op.create_index(op.f("ix_group_meal_plan_rules_household_id"), ["household_id"], unique=False)
|
||||
batch_op.create_foreign_key("fk_group_meal_plan_rules_household_id", "households", ["household_id"], ["id"])
|
||||
|
||||
with op.batch_alter_table("invite_tokens") as batch_op:
|
||||
batch_op.add_column(sa.Column("household_id", mealie.db.migration_types.GUID(), nullable=True))
|
||||
batch_op.create_index(op.f("ix_invite_tokens_household_id"), ["household_id"], unique=False)
|
||||
batch_op.create_foreign_key("fk_invite_tokens_household_id", "households", ["household_id"], ["id"])
|
||||
|
||||
with op.batch_alter_table("recipe_actions") as batch_op:
|
||||
batch_op.add_column(sa.Column("household_id", mealie.db.migration_types.GUID(), nullable=True))
|
||||
batch_op.create_index(op.f("ix_recipe_actions_household_id"), ["household_id"], unique=False)
|
||||
batch_op.create_foreign_key("fk_recipe_actions_household_id", "households", ["household_id"], ["id"])
|
||||
|
||||
with op.batch_alter_table("users") as batch_op:
|
||||
batch_op.add_column(sa.Column("household_id", mealie.db.migration_types.GUID(), nullable=True))
|
||||
batch_op.create_index(op.f("ix_users_household_id"), ["household_id"], unique=False)
|
||||
batch_op.create_foreign_key("fk_users_household_id", "households", ["household_id"], ["id"])
|
||||
|
||||
with op.batch_alter_table("webhook_urls") as batch_op:
|
||||
batch_op.add_column(sa.Column("household_id", mealie.db.migration_types.GUID(), nullable=True))
|
||||
batch_op.create_index(op.f("ix_webhook_urls_household_id"), ["household_id"], unique=False)
|
||||
batch_op.create_foreign_key("fk_webhook_urls_household_id", "households", ["household_id"], ["id"])
|
||||
# ### end Alembic commands ###
|
||||
|
||||
populate_household_data()
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, "webhook_urls", type_="foreignkey")
|
||||
op.drop_index(op.f("ix_webhook_urls_household_id"), table_name="webhook_urls")
|
||||
op.drop_column("webhook_urls", "household_id")
|
||||
op.drop_constraint(None, "users", type_="foreignkey")
|
||||
op.drop_index(op.f("ix_users_household_id"), table_name="users")
|
||||
op.drop_column("users", "household_id")
|
||||
op.drop_constraint(None, "recipe_actions", type_="foreignkey")
|
||||
op.drop_index(op.f("ix_recipe_actions_household_id"), table_name="recipe_actions")
|
||||
op.drop_column("recipe_actions", "household_id")
|
||||
op.drop_constraint(None, "invite_tokens", type_="foreignkey")
|
||||
op.drop_index(op.f("ix_invite_tokens_household_id"), table_name="invite_tokens")
|
||||
op.drop_column("invite_tokens", "household_id")
|
||||
op.drop_constraint(None, "group_meal_plan_rules", type_="foreignkey")
|
||||
op.drop_index(op.f("ix_group_meal_plan_rules_household_id"), table_name="group_meal_plan_rules")
|
||||
op.drop_column("group_meal_plan_rules", "household_id")
|
||||
op.drop_constraint(None, "group_events_notifiers", type_="foreignkey")
|
||||
op.drop_index(op.f("ix_group_events_notifiers_household_id"), table_name="group_events_notifiers")
|
||||
op.drop_column("group_events_notifiers", "household_id")
|
||||
op.drop_constraint(None, "cookbooks", type_="foreignkey")
|
||||
op.drop_index(op.f("ix_cookbooks_household_id"), table_name="cookbooks")
|
||||
op.drop_column("cookbooks", "household_id")
|
||||
op.drop_constraint("cookbook_slug_group_id_key", "cookbooks", type_="unique")
|
||||
op.drop_index(op.f("ix_household_preferences_household_id"), table_name="household_preferences")
|
||||
op.drop_index(op.f("ix_household_preferences_created_at"), table_name="household_preferences")
|
||||
op.drop_table("household_preferences")
|
||||
op.drop_index(op.f("ix_households_slug"), table_name="households")
|
||||
op.drop_index(op.f("ix_households_name"), table_name="households")
|
||||
op.drop_index(op.f("ix_households_group_id"), table_name="households")
|
||||
op.drop_index(op.f("ix_households_created_at"), table_name="households")
|
||||
op.drop_table("households")
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,74 @@
|
||||
"""added household recipe lock setting and household management user permission
|
||||
|
||||
Revision ID: be568e39ffdf
|
||||
Revises: feecc8ffb956
|
||||
Create Date: 2024-09-02 21:39:49.210355
|
||||
|
||||
"""
|
||||
|
||||
from textwrap import dedent
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "be568e39ffdf"
|
||||
down_revision = "feecc8ffb956"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def populate_defaults():
|
||||
if op.get_context().dialect.name == "postgresql":
|
||||
TRUE = "TRUE"
|
||||
FALSE = "FALSE"
|
||||
else:
|
||||
TRUE = "1"
|
||||
FALSE = "0"
|
||||
|
||||
op.execute(
|
||||
dedent(
|
||||
f"""
|
||||
UPDATE household_preferences
|
||||
SET lock_recipe_edits_from_other_households = {TRUE}
|
||||
WHERE lock_recipe_edits_from_other_households IS NULL
|
||||
"""
|
||||
)
|
||||
)
|
||||
op.execute(
|
||||
dedent(
|
||||
f"""
|
||||
UPDATE users
|
||||
SET can_manage_household = {FALSE}
|
||||
WHERE can_manage_household IS NULL AND admin = {FALSE}
|
||||
"""
|
||||
)
|
||||
)
|
||||
op.execute(
|
||||
dedent(
|
||||
f"""
|
||||
UPDATE users
|
||||
SET can_manage_household = {TRUE}
|
||||
WHERE can_manage_household IS NULL AND admin = {TRUE}
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column(
|
||||
"household_preferences",
|
||||
sa.Column("lock_recipe_edits_from_other_households", sa.Boolean(), nullable=True),
|
||||
)
|
||||
op.add_column("users", sa.Column("can_manage_household", sa.Boolean(), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
populate_defaults()
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column("users", "can_manage_household")
|
||||
op.drop_column("household_preferences", "lock_recipe_edits_from_other_households")
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,53 @@
|
||||
"""add households filter to meal plans
|
||||
|
||||
Revision ID: 1fe4bd37ccc8
|
||||
Revises: be568e39ffdf
|
||||
Create Date: 2024-09-18 14:52:55.831540
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
import mealie.db.migration_types
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "1fe4bd37ccc8"
|
||||
down_revision: str | None = "be568e39ffdf"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table(
|
||||
"plan_rules_to_households",
|
||||
sa.Column("group_plan_rule_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.Column("household_id", mealie.db.migration_types.GUID(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_plan_rule_id"],
|
||||
["group_meal_plan_rules.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["household_id"],
|
||||
["households.id"],
|
||||
),
|
||||
sa.UniqueConstraint("group_plan_rule_id", "household_id", name="group_plan_rule_id_household_id_key"),
|
||||
)
|
||||
with op.batch_alter_table("plan_rules_to_households", schema=None) as batch_op:
|
||||
batch_op.create_index(
|
||||
batch_op.f("ix_plan_rules_to_households_group_plan_rule_id"), ["group_plan_rule_id"], unique=False
|
||||
)
|
||||
batch_op.create_index(batch_op.f("ix_plan_rules_to_households_household_id"), ["household_id"], unique=False)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table("plan_rules_to_households", schema=None) as batch_op:
|
||||
batch_op.drop_index(batch_op.f("ix_plan_rules_to_households_household_id"))
|
||||
batch_op.drop_index(batch_op.f("ix_plan_rules_to_households_group_plan_rule_id"))
|
||||
|
||||
op.drop_table("plan_rules_to_households")
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,38 @@
|
||||
"""'add the rest of the schema.org nutrition properties'
|
||||
|
||||
Revision ID: 602927e1013e
|
||||
Revises: 1fe4bd37ccc8
|
||||
Create Date: 2024-10-01 14:17:00.611398
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "602927e1013e"
|
||||
down_revision: str | None = "1fe4bd37ccc8"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table("recipe_nutrition", schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column("cholesterol_content", sa.String(), nullable=True))
|
||||
batch_op.add_column(sa.Column("saturated_fat_content", sa.String(), nullable=True))
|
||||
batch_op.add_column(sa.Column("trans_fat_content", sa.String(), nullable=True))
|
||||
batch_op.add_column(sa.Column("unsaturated_fat_content", sa.String(), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table("recipe_nutrition", schema=None) as batch_op:
|
||||
batch_op.drop_column("unsaturated_fat_content")
|
||||
batch_op.drop_column("trans_fat_content")
|
||||
batch_op.drop_column("saturated_fat_content")
|
||||
batch_op.drop_column("cholesterol_content")
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,188 @@
|
||||
"""added query_filter_string to cookbook and mealplan
|
||||
|
||||
Revision ID: 86054b40fd06
|
||||
Revises: 602927e1013e
|
||||
Create Date: 2024-10-08 21:17:31.601903
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
from sqlalchemy import orm
|
||||
|
||||
from mealie.db.models._model_utils import guid
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "86054b40fd06"
|
||||
down_revision: str | None = "602927e1013e"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
# Intermediate table definitions
|
||||
class SqlAlchemyBase(orm.DeclarativeBase):
|
||||
pass
|
||||
|
||||
|
||||
class Category(SqlAlchemyBase):
|
||||
__tablename__ = "categories"
|
||||
id: orm.Mapped[guid.GUID] = orm.mapped_column(guid.GUID, primary_key=True, default=guid.GUID.generate)
|
||||
|
||||
|
||||
class Tag(SqlAlchemyBase):
|
||||
__tablename__ = "tags"
|
||||
id: orm.Mapped[guid.GUID] = orm.mapped_column(guid.GUID, primary_key=True, default=guid.GUID.generate)
|
||||
|
||||
|
||||
class Tool(SqlAlchemyBase):
|
||||
__tablename__ = "tools"
|
||||
id: orm.Mapped[guid.GUID] = orm.mapped_column(guid.GUID, primary_key=True, default=guid.GUID.generate)
|
||||
|
||||
|
||||
class Household(SqlAlchemyBase):
|
||||
__tablename__ = "households"
|
||||
id: orm.Mapped[guid.GUID] = orm.mapped_column(guid.GUID, primary_key=True, default=guid.GUID.generate)
|
||||
|
||||
|
||||
cookbooks_to_categories = sa.Table(
|
||||
"cookbooks_to_categories",
|
||||
SqlAlchemyBase.metadata,
|
||||
sa.Column("cookbook_id", guid.GUID, sa.ForeignKey("cookbooks.id"), index=True),
|
||||
sa.Column("category_id", guid.GUID, sa.ForeignKey("categories.id"), index=True),
|
||||
)
|
||||
|
||||
cookbooks_to_tags = sa.Table(
|
||||
"cookbooks_to_tags",
|
||||
SqlAlchemyBase.metadata,
|
||||
sa.Column("cookbook_id", guid.GUID, sa.ForeignKey("cookbooks.id"), index=True),
|
||||
sa.Column("tag_id", guid.GUID, sa.ForeignKey("tags.id"), index=True),
|
||||
)
|
||||
|
||||
cookbooks_to_tools = sa.Table(
|
||||
"cookbooks_to_tools",
|
||||
SqlAlchemyBase.metadata,
|
||||
sa.Column("cookbook_id", guid.GUID, sa.ForeignKey("cookbooks.id"), index=True),
|
||||
sa.Column("tool_id", guid.GUID, sa.ForeignKey("tools.id"), index=True),
|
||||
)
|
||||
|
||||
plan_rules_to_categories = sa.Table(
|
||||
"plan_rules_to_categories",
|
||||
SqlAlchemyBase.metadata,
|
||||
sa.Column("group_plan_rule_id", guid.GUID, sa.ForeignKey("group_meal_plan_rules.id"), index=True),
|
||||
sa.Column("category_id", guid.GUID, sa.ForeignKey("categories.id"), index=True),
|
||||
)
|
||||
|
||||
plan_rules_to_tags = sa.Table(
|
||||
"plan_rules_to_tags",
|
||||
SqlAlchemyBase.metadata,
|
||||
sa.Column("plan_rule_id", guid.GUID, sa.ForeignKey("group_meal_plan_rules.id"), index=True),
|
||||
sa.Column("tag_id", guid.GUID, sa.ForeignKey("tags.id"), index=True),
|
||||
)
|
||||
|
||||
plan_rules_to_households = sa.Table(
|
||||
"plan_rules_to_households",
|
||||
SqlAlchemyBase.metadata,
|
||||
sa.Column("group_plan_rule_id", guid.GUID, sa.ForeignKey("group_meal_plan_rules.id"), index=True),
|
||||
sa.Column("household_id", guid.GUID, sa.ForeignKey("households.id"), index=True),
|
||||
)
|
||||
|
||||
|
||||
class CookBook(SqlAlchemyBase):
|
||||
__tablename__ = "cookbooks"
|
||||
|
||||
id: orm.Mapped[guid.GUID] = orm.mapped_column(guid.GUID, primary_key=True, default=guid.GUID.generate)
|
||||
query_filter_string: orm.Mapped[str] = orm.mapped_column(sa.String, nullable=False, default="")
|
||||
|
||||
categories: orm.Mapped[list[Category]] = orm.relationship(
|
||||
Category, secondary=cookbooks_to_categories, single_parent=True
|
||||
)
|
||||
require_all_categories: orm.Mapped[bool | None] = orm.mapped_column(sa.Boolean, default=True)
|
||||
|
||||
tags: orm.Mapped[list[Tag]] = orm.relationship(Tag, secondary=cookbooks_to_tags, single_parent=True)
|
||||
require_all_tags: orm.Mapped[bool | None] = orm.mapped_column(sa.Boolean, default=True)
|
||||
|
||||
tools: orm.Mapped[list[Tool]] = orm.relationship(Tool, secondary=cookbooks_to_tools, single_parent=True)
|
||||
require_all_tools: orm.Mapped[bool | None] = orm.mapped_column(sa.Boolean, default=True)
|
||||
|
||||
|
||||
class GroupMealPlanRules(SqlAlchemyBase):
|
||||
__tablename__ = "group_meal_plan_rules"
|
||||
|
||||
id: orm.Mapped[guid.GUID] = orm.mapped_column(guid.GUID, primary_key=True, default=guid.GUID.generate)
|
||||
query_filter_string: orm.Mapped[str] = orm.mapped_column(sa.String, nullable=False, default="")
|
||||
|
||||
categories: orm.Mapped[list[Category]] = orm.relationship(Category, secondary=plan_rules_to_categories)
|
||||
tags: orm.Mapped[list[Tag]] = orm.relationship(Tag, secondary=plan_rules_to_tags)
|
||||
households: orm.Mapped[list["Household"]] = orm.relationship("Household", secondary=plan_rules_to_households)
|
||||
|
||||
|
||||
def migrate_cookbooks():
|
||||
bind = op.get_bind()
|
||||
session = orm.Session(bind=bind)
|
||||
|
||||
cookbooks = session.query(CookBook).all()
|
||||
for cookbook in cookbooks:
|
||||
parts = []
|
||||
if cookbook.categories:
|
||||
relop = "CONTAINS ALL" if cookbook.require_all_categories else "IN"
|
||||
vals = ",".join([f'"{cat.id}"' for cat in cookbook.categories])
|
||||
parts.append(f"recipe_category.id {relop} [{vals}]")
|
||||
if cookbook.tags:
|
||||
relop = "CONTAINS ALL" if cookbook.require_all_tags else "IN"
|
||||
vals = ",".join([f'"{tag.id}"' for tag in cookbook.tags])
|
||||
parts.append(f"tags.id {relop} [{vals}]")
|
||||
if cookbook.tools:
|
||||
relop = "CONTAINS ALL" if cookbook.require_all_tools else "IN"
|
||||
vals = ",".join([f'"{tool.id}"' for tool in cookbook.tools])
|
||||
parts.append(f"tools.id {relop} [{vals}]")
|
||||
|
||||
cookbook.query_filter_string = " AND ".join(parts)
|
||||
|
||||
session.commit()
|
||||
|
||||
|
||||
def migrate_mealplan_rules():
|
||||
bind = op.get_bind()
|
||||
session = orm.Session(bind=bind)
|
||||
|
||||
rules = session.query(GroupMealPlanRules).all()
|
||||
for rule in rules:
|
||||
parts = []
|
||||
if rule.categories:
|
||||
vals = ",".join([f'"{cat.id}"' for cat in rule.categories])
|
||||
parts.append(f"recipe_category.id CONTAINS ALL [{vals}]")
|
||||
if rule.tags:
|
||||
vals = ",".join([f'"{tag.id}"' for tag in rule.tags])
|
||||
parts.append(f"tags.id CONTAINS ALL [{vals}]")
|
||||
if rule.households:
|
||||
vals = ",".join([f'"{household.id}"' for household in rule.households])
|
||||
parts.append(f"household_id IN [{vals}]")
|
||||
|
||||
rule.query_filter_string = " AND ".join(parts)
|
||||
|
||||
session.commit()
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table("cookbooks", schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column("query_filter_string", sa.String(), nullable=False, server_default=""))
|
||||
|
||||
with op.batch_alter_table("group_meal_plan_rules", schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column("query_filter_string", sa.String(), nullable=False, server_default=""))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
migrate_cookbooks()
|
||||
migrate_mealplan_rules()
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table("group_meal_plan_rules", schema=None) as batch_op:
|
||||
batch_op.drop_column("query_filter_string")
|
||||
|
||||
with op.batch_alter_table("cookbooks", schema=None) as batch_op:
|
||||
batch_op.drop_column("query_filter_string")
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,32 @@
|
||||
"""'Add summary to recipe instructions'
|
||||
|
||||
Revision ID: 3897397b4631
|
||||
Revises: 86054b40fd06
|
||||
Create Date: 2024-10-20 09:47:46.844436
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "3897397b4631"
|
||||
down_revision: str | None = "86054b40fd06"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table("recipe_instructions", schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column("summary", sa.String(), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table("recipe_instructions", schema=None) as batch_op:
|
||||
batch_op.drop_column("summary")
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,72 @@
|
||||
"""add recipe yield quantity
|
||||
|
||||
Revision ID: b1020f328e98
|
||||
Revises: 3897397b4631
|
||||
Create Date: 2024-10-23 15:50:59.888793
|
||||
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
from sqlalchemy import orm
|
||||
|
||||
from mealie.db.models._model_utils.guid import GUID
|
||||
from mealie.services.scraper.cleaner import clean_yield
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "b1020f328e98"
|
||||
down_revision: str | None = "3897397b4631"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
# Intermediate table definitions
|
||||
class SqlAlchemyBase(orm.DeclarativeBase):
|
||||
pass
|
||||
|
||||
|
||||
class RecipeModel(SqlAlchemyBase):
|
||||
__tablename__ = "recipes"
|
||||
|
||||
id: orm.Mapped[GUID] = orm.mapped_column(GUID, primary_key=True, default=GUID.generate)
|
||||
recipe_yield: orm.Mapped[str | None] = orm.mapped_column(sa.String)
|
||||
recipe_yield_quantity: orm.Mapped[float] = orm.mapped_column(sa.Float, index=True, default=0)
|
||||
recipe_servings: orm.Mapped[float] = orm.mapped_column(sa.Float, index=True, default=0)
|
||||
|
||||
|
||||
def parse_recipe_yields():
|
||||
bind = op.get_bind()
|
||||
session = orm.Session(bind=bind)
|
||||
|
||||
for recipe in session.query(RecipeModel).all():
|
||||
try:
|
||||
recipe.recipe_servings, recipe.recipe_yield_quantity, recipe.recipe_yield = clean_yield(recipe.recipe_yield)
|
||||
except Exception:
|
||||
recipe.recipe_servings = 0
|
||||
recipe.recipe_yield_quantity = 0
|
||||
|
||||
session.commit()
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table("recipes", schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column("recipe_yield_quantity", sa.Float(), nullable=False, server_default="0"))
|
||||
batch_op.create_index(batch_op.f("ix_recipes_recipe_yield_quantity"), ["recipe_yield_quantity"], unique=False)
|
||||
batch_op.add_column(sa.Column("recipe_servings", sa.Float(), nullable=False, server_default="0"))
|
||||
batch_op.create_index(batch_op.f("ix_recipes_recipe_servings"), ["recipe_servings"], unique=False)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
parse_recipe_yields()
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table("recipes", schema=None) as batch_op:
|
||||
batch_op.drop_index(batch_op.f("ix_recipes_recipe_servings"))
|
||||
batch_op.drop_column("recipe_servings")
|
||||
batch_op.drop_index(batch_op.f("ix_recipes_recipe_yield_quantity"))
|
||||
batch_op.drop_column("recipe_yield_quantity")
|
||||
|
||||
# ### end Alembic commands ###
|
||||
Reference in New Issue
Block a user