From 5cf54c1a08b094a8a0996835a4d5fcbd2861bba2 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Sun, 19 Dec 2021 22:22:05 +0100 Subject: [PATCH] feat(db): Add migrations support to plugins * Add initial migrations for core Flaschengeist * Add migrations to balance * Add migrations to pricelist --- flaschengeist/database.py | 1 - flaschengeist/plugins/balance/__init__.py | 3 + .../f07df84f7a95_initial_balance_migration.py | 47 ++++++ flaschengeist/plugins/pricelist/__init__.py | 2 + ...d9d306be676_initial_pricelist_migration.py | 141 ++++++++++++++++++ .../d3026757c7cb_initial_migration.py | 141 ++++++++++++++++++ 6 files changed, 334 insertions(+), 1 deletion(-) create mode 100644 flaschengeist/plugins/balance/migrations/f07df84f7a95_initial_balance_migration.py create mode 100644 flaschengeist/plugins/pricelist/migrations/7d9d306be676_initial_pricelist_migration.py create mode 100644 migrations/versions/d3026757c7cb_initial_migration.py diff --git a/flaschengeist/database.py b/flaschengeist/database.py index 4da914a..f264715 100644 --- a/flaschengeist/database.py +++ b/flaschengeist/database.py @@ -36,7 +36,6 @@ def configure_alembic(config): # write back seperator (we changed it if neither seperator nor locations were specified) config.set_main_option("version_path_separator", sep) config.set_main_option("version_locations", sep.join(migrations)) - print(config.get_main_option("version_locations")) return config diff --git a/flaschengeist/plugins/balance/__init__.py b/flaschengeist/plugins/balance/__init__.py index c430398..3678ded 100644 --- a/flaschengeist/plugins/balance/__init__.py +++ b/flaschengeist/plugins/balance/__init__.py @@ -3,6 +3,7 @@ Extends users plugin with balance functions """ +import pathlib from flask import Blueprint, current_app from werkzeug.local import LocalProxy from werkzeug.exceptions import NotFound @@ -66,6 +67,8 @@ class BalancePlugin(Plugin): self.blueprint = blueprint + self.migrations_path = (pathlib.Path(__file__).parent / "migrations").resolve() + @plugins_loaded def post_loaded(*args, **kwargs): if config.get("allow_service_debit", False) and "events" in current_app.config["FG_PLUGINS"]: diff --git a/flaschengeist/plugins/balance/migrations/f07df84f7a95_initial_balance_migration.py b/flaschengeist/plugins/balance/migrations/f07df84f7a95_initial_balance_migration.py new file mode 100644 index 0000000..ecde05e --- /dev/null +++ b/flaschengeist/plugins/balance/migrations/f07df84f7a95_initial_balance_migration.py @@ -0,0 +1,47 @@ +"""Initial balance migration + +Revision ID: f07df84f7a95 +Revises: +Create Date: 2021-12-19 21:12:53.192267 + +""" +from alembic import op +import sqlalchemy as sa +import flaschengeist + + +# revision identifiers, used by Alembic. +revision = "f07df84f7a95" +down_revision = None +branch_labels = ("balance",) +depends_on = "d3026757c7cb" + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "balance_transaction", + sa.Column("receiver_id", flaschengeist.models.Serial(), nullable=True), + sa.Column("sender_id", flaschengeist.models.Serial(), nullable=True), + sa.Column("author_id", flaschengeist.models.Serial(), nullable=False), + sa.Column("id", flaschengeist.models.Serial(), nullable=False), + sa.Column("time", flaschengeist.models.UtcDateTime(), nullable=False), + sa.Column("amount", sa.Numeric(precision=5, scale=2, asdecimal=False), nullable=False), + sa.Column("reversal_id", flaschengeist.models.Serial(), nullable=True), + sa.ForeignKeyConstraint(["author_id"], ["user.id"], name=op.f("fk_balance_transaction_author_id_user")), + sa.ForeignKeyConstraint(["receiver_id"], ["user.id"], name=op.f("fk_balance_transaction_receiver_id_user")), + sa.ForeignKeyConstraint( + ["reversal_id"], + ["balance_transaction.id"], + name=op.f("fk_balance_transaction_reversal_id_balance_transaction"), + ), + sa.ForeignKeyConstraint(["sender_id"], ["user.id"], name=op.f("fk_balance_transaction_sender_id_user")), + sa.PrimaryKeyConstraint("id", name=op.f("pk_balance_transaction")), + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table("balance_transaction") + # ### end Alembic commands ### diff --git a/flaschengeist/plugins/pricelist/__init__.py b/flaschengeist/plugins/pricelist/__init__.py index d06af7f..3f45351 100644 --- a/flaschengeist/plugins/pricelist/__init__.py +++ b/flaschengeist/plugins/pricelist/__init__.py @@ -1,5 +1,6 @@ """Pricelist plugin""" +import pathlib from flask import Blueprint, jsonify, request, current_app from werkzeug.local import LocalProxy from werkzeug.exceptions import BadRequest, Forbidden, NotFound, Unauthorized @@ -26,6 +27,7 @@ class PriceListPlugin(Plugin): def __init__(self, entry_point, config=None): super().__init__(entry_point, config) self.blueprint = blueprint + self.migrations_path = (pathlib.Path(__file__).parent / "migrations").resolve() config = {"discount": 0} config.update(config) diff --git a/flaschengeist/plugins/pricelist/migrations/7d9d306be676_initial_pricelist_migration.py b/flaschengeist/plugins/pricelist/migrations/7d9d306be676_initial_pricelist_migration.py new file mode 100644 index 0000000..c9da61e --- /dev/null +++ b/flaschengeist/plugins/pricelist/migrations/7d9d306be676_initial_pricelist_migration.py @@ -0,0 +1,141 @@ +"""Initial pricelist migration + +Revision ID: 7d9d306be676 +Revises: +Create Date: 2021-12-19 21:43:30.203811 + +""" +from alembic import op +import sqlalchemy as sa +import flaschengeist + + +# revision identifiers, used by Alembic. +revision = "7d9d306be676" +down_revision = None +branch_labels = ("pricelist",) +depends_on = "d3026757c7cb" + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "drink_extra_ingredient", + sa.Column("id", flaschengeist.models.Serial(), nullable=False), + sa.Column("name", sa.String(length=30), nullable=False), + sa.Column("price", sa.Numeric(precision=5, scale=2, asdecimal=False), nullable=True), + sa.PrimaryKeyConstraint("id", name=op.f("pk_drink_extra_ingredient")), + sa.UniqueConstraint("name", name=op.f("uq_drink_extra_ingredient_name")), + ) + op.create_table( + "drink_tag", + sa.Column("id", flaschengeist.models.Serial(), nullable=False), + sa.Column("name", sa.String(length=30), nullable=False), + sa.Column("color", sa.String(length=7), nullable=False), + sa.PrimaryKeyConstraint("id", name=op.f("pk_drink_tag")), + sa.UniqueConstraint("name", name=op.f("uq_drink_tag_name")), + ) + op.create_table( + "drink_type", + sa.Column("id", flaschengeist.models.Serial(), nullable=False), + sa.Column("name", sa.String(length=30), nullable=False), + sa.PrimaryKeyConstraint("id", name=op.f("pk_drink_type")), + sa.UniqueConstraint("name", name=op.f("uq_drink_type_name")), + ) + op.create_table( + "drink", + sa.Column("id", flaschengeist.models.Serial(), nullable=False), + sa.Column("article_id", sa.String(length=64), nullable=True), + sa.Column("package_size", sa.Integer(), nullable=True), + sa.Column("name", sa.String(length=60), nullable=False), + sa.Column("volume", sa.Numeric(precision=5, scale=2, asdecimal=False), nullable=True), + sa.Column("cost_per_volume", sa.Numeric(precision=5, scale=3, asdecimal=False), nullable=True), + sa.Column("cost_per_package", sa.Numeric(precision=5, scale=3, asdecimal=False), nullable=True), + sa.Column("receipt", sa.PickleType(), nullable=True), + sa.Column("type_id", flaschengeist.models.Serial(), nullable=True), + sa.Column("image_id", flaschengeist.models.Serial(), nullable=True), + sa.ForeignKeyConstraint(["image_id"], ["image.id"], name=op.f("fk_drink_image_id_image")), + sa.ForeignKeyConstraint(["type_id"], ["drink_type.id"], name=op.f("fk_drink_type_id_drink_type")), + sa.PrimaryKeyConstraint("id", name=op.f("pk_drink")), + ) + op.create_table( + "drink_ingredient", + sa.Column("id", flaschengeist.models.Serial(), nullable=False), + sa.Column("volume", sa.Numeric(precision=5, scale=2, asdecimal=False), nullable=False), + sa.Column("ingredient_id", flaschengeist.models.Serial(), nullable=True), + sa.ForeignKeyConstraint(["ingredient_id"], ["drink.id"], name=op.f("fk_drink_ingredient_ingredient_id_drink")), + sa.PrimaryKeyConstraint("id", name=op.f("pk_drink_ingredient")), + ) + op.create_table( + "drink_price_volume", + sa.Column("id", flaschengeist.models.Serial(), nullable=False), + sa.Column("drink_id", flaschengeist.models.Serial(), nullable=True), + sa.Column("volume", sa.Numeric(precision=5, scale=2, asdecimal=False), nullable=True), + sa.ForeignKeyConstraint(["drink_id"], ["drink.id"], name=op.f("fk_drink_price_volume_drink_id_drink")), + sa.PrimaryKeyConstraint("id", name=op.f("pk_drink_price_volume")), + ) + op.create_table( + "drink_x_tag", + sa.Column("drink_id", flaschengeist.models.Serial(), nullable=True), + sa.Column("tag_id", flaschengeist.models.Serial(), nullable=True), + sa.ForeignKeyConstraint(["drink_id"], ["drink.id"], name=op.f("fk_drink_x_tag_drink_id_drink")), + sa.ForeignKeyConstraint(["tag_id"], ["drink_tag.id"], name=op.f("fk_drink_x_tag_tag_id_drink_tag")), + ) + op.create_table( + "drink_x_type", + sa.Column("drink_id", flaschengeist.models.Serial(), nullable=True), + sa.Column("type_id", flaschengeist.models.Serial(), nullable=True), + sa.ForeignKeyConstraint(["drink_id"], ["drink.id"], name=op.f("fk_drink_x_type_drink_id_drink")), + sa.ForeignKeyConstraint(["type_id"], ["drink_type.id"], name=op.f("fk_drink_x_type_type_id_drink_type")), + ) + op.create_table( + "drink_ingredient_association", + sa.Column("id", flaschengeist.models.Serial(), nullable=False), + sa.Column("volume_id", flaschengeist.models.Serial(), nullable=True), + sa.Column("_drink_ingredient_id", flaschengeist.models.Serial(), nullable=True), + sa.Column("_extra_ingredient_id", flaschengeist.models.Serial(), nullable=True), + sa.ForeignKeyConstraint( + ["_drink_ingredient_id"], + ["drink_ingredient.id"], + name=op.f("fk_drink_ingredient_association__drink_ingredient_id_drink_ingredient"), + ), + sa.ForeignKeyConstraint( + ["_extra_ingredient_id"], + ["drink_extra_ingredient.id"], + name=op.f("fk_drink_ingredient_association__extra_ingredient_id_drink_extra_ingredient"), + ), + sa.ForeignKeyConstraint( + ["volume_id"], + ["drink_price_volume.id"], + name=op.f("fk_drink_ingredient_association_volume_id_drink_price_volume"), + ), + sa.PrimaryKeyConstraint("id", name=op.f("pk_drink_ingredient_association")), + ) + op.create_table( + "drink_price", + sa.Column("id", flaschengeist.models.Serial(), nullable=False), + sa.Column("price", sa.Numeric(precision=5, scale=2, asdecimal=False), nullable=True), + sa.Column("volume_id", flaschengeist.models.Serial(), nullable=True), + sa.Column("public", sa.Boolean(), nullable=True), + sa.Column("description", sa.String(length=30), nullable=True), + sa.ForeignKeyConstraint( + ["volume_id"], ["drink_price_volume.id"], name=op.f("fk_drink_price_volume_id_drink_price_volume") + ), + sa.PrimaryKeyConstraint("id", name=op.f("pk_drink_price")), + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table("drink_price") + op.drop_table("drink_ingredient_association") + op.drop_table("drink_x_type") + op.drop_table("drink_x_tag") + op.drop_table("drink_price_volume") + op.drop_table("drink_ingredient") + op.drop_table("drink") + op.drop_table("drink_type") + op.drop_table("drink_tag") + op.drop_table("drink_extra_ingredient") + # ### end Alembic commands ### diff --git a/migrations/versions/d3026757c7cb_initial_migration.py b/migrations/versions/d3026757c7cb_initial_migration.py new file mode 100644 index 0000000..23f55b5 --- /dev/null +++ b/migrations/versions/d3026757c7cb_initial_migration.py @@ -0,0 +1,141 @@ +"""Initial migration. + +Revision ID: d3026757c7cb +Revises: +Create Date: 2021-12-19 20:34:34.122576 + +""" +from alembic import op +import sqlalchemy as sa +import flaschengeist + + +# revision identifiers, used by Alembic. +revision = "d3026757c7cb" +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "image", + sa.Column("id", flaschengeist.models.Serial(), nullable=False), + sa.Column("filename_", sa.String(length=127), nullable=False), + sa.Column("mimetype_", sa.String(length=30), nullable=False), + sa.Column("thumbnail_", sa.String(length=127), nullable=True), + sa.Column("path_", sa.String(length=127), nullable=True), + sa.PrimaryKeyConstraint("id", name=op.f("pk_image")), + ) + op.create_table( + "permission", + sa.Column("name", sa.String(length=30), nullable=True), + sa.Column("id", flaschengeist.models.Serial(), nullable=False), + sa.PrimaryKeyConstraint("id", name=op.f("pk_permission")), + sa.UniqueConstraint("name", name=op.f("uq_permission_name")), + ) + op.create_table( + "plugin_setting", + sa.Column("id", flaschengeist.models.Serial(), nullable=False), + sa.Column("plugin", sa.String(length=30), nullable=True), + sa.Column("name", sa.String(length=30), nullable=False), + sa.Column("value", sa.PickleType(), nullable=True), + sa.PrimaryKeyConstraint("id", name=op.f("pk_plugin_setting")), + ) + op.create_table( + "role", + sa.Column("id", flaschengeist.models.Serial(), nullable=False), + sa.Column("name", sa.String(length=30), nullable=True), + sa.PrimaryKeyConstraint("id", name=op.f("pk_role")), + sa.UniqueConstraint("name", name=op.f("uq_role_name")), + ) + op.create_table( + "role_x_permission", + sa.Column("role_id", flaschengeist.models.Serial(), nullable=True), + sa.Column("permission_id", flaschengeist.models.Serial(), nullable=True), + sa.ForeignKeyConstraint( + ["permission_id"], ["permission.id"], name=op.f("fk_role_x_permission_permission_id_permission") + ), + sa.ForeignKeyConstraint(["role_id"], ["role.id"], name=op.f("fk_role_x_permission_role_id_role")), + ) + op.create_table( + "user", + sa.Column("userid", sa.String(length=30), nullable=False), + sa.Column("display_name", sa.String(length=30), nullable=True), + sa.Column("firstname", sa.String(length=50), nullable=False), + sa.Column("lastname", sa.String(length=50), nullable=False), + sa.Column("deleted", sa.Boolean(), nullable=True), + sa.Column("birthday", sa.Date(), nullable=True), + sa.Column("mail", sa.String(length=60), nullable=True), + sa.Column("id", flaschengeist.models.Serial(), nullable=False), + sa.Column("avatar", flaschengeist.models.Serial(), nullable=True), + sa.ForeignKeyConstraint(["avatar"], ["image.id"], name=op.f("fk_user_avatar_image")), + sa.PrimaryKeyConstraint("id", name=op.f("pk_user")), + sa.UniqueConstraint("userid", name=op.f("uq_user_userid")), + ) + op.create_table( + "notification", + sa.Column("id", flaschengeist.models.Serial(), nullable=False), + sa.Column("plugin", sa.String(length=127), nullable=False), + sa.Column("text", sa.Text(), nullable=True), + sa.Column("data", sa.PickleType(), nullable=True), + sa.Column("time", flaschengeist.models.UtcDateTime(), nullable=False), + sa.Column("user_id", flaschengeist.models.Serial(), nullable=False), + sa.ForeignKeyConstraint(["user_id"], ["user.id"], name=op.f("fk_notification_user_id_user")), + sa.PrimaryKeyConstraint("id", name=op.f("pk_notification")), + ) + op.create_table( + "password_reset", + sa.Column("user", flaschengeist.models.Serial(), nullable=False), + sa.Column("token", sa.String(length=32), nullable=True), + sa.Column("expires", flaschengeist.models.UtcDateTime(), nullable=True), + sa.ForeignKeyConstraint(["user"], ["user.id"], name=op.f("fk_password_reset_user_user")), + sa.PrimaryKeyConstraint("user", name=op.f("pk_password_reset")), + ) + op.create_table( + "session", + sa.Column("expires", flaschengeist.models.UtcDateTime(), nullable=True), + sa.Column("token", sa.String(length=32), nullable=True), + sa.Column("lifetime", sa.Integer(), nullable=True), + sa.Column("browser", sa.String(length=30), nullable=True), + sa.Column("platform", sa.String(length=30), nullable=True), + sa.Column("id", flaschengeist.models.Serial(), nullable=False), + sa.Column("user_id", flaschengeist.models.Serial(), nullable=True), + sa.ForeignKeyConstraint(["user_id"], ["user.id"], name=op.f("fk_session_user_id_user")), + sa.PrimaryKeyConstraint("id", name=op.f("pk_session")), + sa.UniqueConstraint("token", name=op.f("uq_session_token")), + ) + op.create_table( + "user_attribute", + sa.Column("id", flaschengeist.models.Serial(), nullable=False), + sa.Column("user", flaschengeist.models.Serial(), nullable=False), + sa.Column("name", sa.String(length=30), nullable=True), + sa.Column("value", sa.PickleType(), nullable=True), + sa.ForeignKeyConstraint(["user"], ["user.id"], name=op.f("fk_user_attribute_user_user")), + sa.PrimaryKeyConstraint("id", name=op.f("pk_user_attribute")), + ) + op.create_table( + "user_x_role", + sa.Column("user_id", flaschengeist.models.Serial(), nullable=True), + sa.Column("role_id", flaschengeist.models.Serial(), nullable=True), + sa.ForeignKeyConstraint(["role_id"], ["role.id"], name=op.f("fk_user_x_role_role_id_role")), + sa.ForeignKeyConstraint(["user_id"], ["user.id"], name=op.f("fk_user_x_role_user_id_user")), + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table("user_x_role") + op.drop_table("user_attribute") + op.drop_table("session") + op.drop_table("password_reset") + op.drop_table("notification") + op.drop_table("user") + op.drop_table("role_x_permission") + op.drop_table("role") + op.drop_table("plugin_setting") + op.drop_table("permission") + op.drop_table("image") + # ### end Alembic commands ###