Private
Public Access
forked from FOSS/AutoShop-Djimbo-Simple
Initial local state
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
# - *- coding: utf- 8 - *-
|
||||
import asyncio
|
||||
from logging.config import fileConfig
|
||||
from typing import Any, List, Optional
|
||||
|
||||
from alembic import context
|
||||
from alembic.operations import ops
|
||||
from sqlalchemy import pool, text
|
||||
from sqlalchemy.engine import Connection
|
||||
from sqlalchemy.schema import DefaultClause
|
||||
from sqlalchemy.ext.asyncio import async_engine_from_config
|
||||
|
||||
from tgbot.database.core import Base
|
||||
|
||||
import tgbot.database # noqa: F401
|
||||
|
||||
config = context.config
|
||||
|
||||
if config.config_file_name is not None:
|
||||
fileConfig(config.config_file_name)
|
||||
|
||||
target_metadata = Base.metadata
|
||||
INDEX_DIFF_NOISE = {"ix_storage_payments_id", "ix_storage_settings_id"}
|
||||
|
||||
|
||||
# Отбрасывание SQLite-шума из автогенерации
|
||||
def _filter_autogenerated_ops(container: Any) -> None:
|
||||
filtered_ops = []
|
||||
|
||||
for operation in container.ops:
|
||||
if isinstance(operation, ops.AlterColumnOp):
|
||||
continue
|
||||
|
||||
if _is_index_noise(operation):
|
||||
continue
|
||||
|
||||
if isinstance(operation, ops.ModifyTableOps):
|
||||
_filter_autogenerated_ops(operation)
|
||||
|
||||
if operation.ops:
|
||||
filtered_ops.append(operation)
|
||||
|
||||
continue
|
||||
|
||||
filtered_ops.append(operation)
|
||||
|
||||
container.ops = filtered_ops
|
||||
|
||||
|
||||
# Подготовка новых колонок к безопасному SQLite ADD COLUMN
|
||||
def _prepare_sqlite_add_columns(container: Any) -> None:
|
||||
for operation in container.ops:
|
||||
if isinstance(operation, ops.AddColumnOp):
|
||||
_apply_server_default(operation.column)
|
||||
continue
|
||||
|
||||
if isinstance(operation, ops.ModifyTableOps):
|
||||
_prepare_sqlite_add_columns(operation)
|
||||
|
||||
|
||||
# Перенос default модели в server_default миграции
|
||||
def _apply_server_default(column: Any) -> None:
|
||||
if column.nullable or column.server_default is not None or column.default is None:
|
||||
return
|
||||
|
||||
default_value = _get_simple_default(column.default.arg)
|
||||
|
||||
if default_value is None:
|
||||
return
|
||||
|
||||
column.server_default = DefaultClause(text(default_value))
|
||||
|
||||
|
||||
# Получение SQL-литерала для простых default-значений
|
||||
def _get_simple_default(value: Any) -> Optional[str]:
|
||||
if callable(value):
|
||||
return None
|
||||
|
||||
if isinstance(value, bool):
|
||||
return "1" if value else "0"
|
||||
|
||||
if isinstance(value, (int, float)):
|
||||
return str(value)
|
||||
|
||||
if isinstance(value, str):
|
||||
return repr(value)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
# Игнорирование индексов, которые SQLite отражает иначе
|
||||
def _is_index_noise(operation: Any) -> bool:
|
||||
if not isinstance(operation, (ops.CreateIndexOp, ops.DropIndexOp)):
|
||||
return False
|
||||
|
||||
return getattr(operation, "index_name", "") in INDEX_DIFF_NOISE
|
||||
|
||||
|
||||
# Ограничение сравнения SQLite только реальными добавлениями/удалениями
|
||||
def _include_object(object_: Any, name: str, type_: str, reflected: bool, compare_to: Any) -> bool:
|
||||
if type_ == "column" and compare_to is not None:
|
||||
return False
|
||||
|
||||
if type_ == "index" and name in INDEX_DIFF_NOISE:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
# Очистка автосозданной миграции перед записью
|
||||
def _process_revision_directives(_context: Any, _revision: Any, directives: List[Any]) -> None:
|
||||
if not directives:
|
||||
return
|
||||
|
||||
script = directives[0]
|
||||
_prepare_sqlite_add_columns(script.upgrade_ops)
|
||||
_filter_autogenerated_ops(script.upgrade_ops)
|
||||
_filter_autogenerated_ops(script.downgrade_ops)
|
||||
|
||||
if not script.upgrade_ops.ops and not script.downgrade_ops.ops:
|
||||
config.attributes["autogenerate_empty"] = True
|
||||
directives.clear()
|
||||
|
||||
|
||||
# Запуск миграций без подключения
|
||||
def run_migrations_offline() -> None:
|
||||
url = config.get_main_option("sqlalchemy.url")
|
||||
|
||||
context.configure(
|
||||
url=url,
|
||||
target_metadata=target_metadata,
|
||||
literal_binds=True,
|
||||
dialect_opts={"paramstyle": "named"},
|
||||
render_as_batch=True,
|
||||
compare_type=False,
|
||||
include_object=_include_object,
|
||||
process_revision_directives=_process_revision_directives,
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
# Запуск миграций на соединении
|
||||
def do_run_migrations(connection: Connection) -> None:
|
||||
context.configure(
|
||||
connection=connection,
|
||||
target_metadata=target_metadata,
|
||||
render_as_batch=True,
|
||||
compare_type=False,
|
||||
include_object=_include_object,
|
||||
process_revision_directives=_process_revision_directives,
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
# Запуск миграций через async-engine
|
||||
async def run_async_migrations() -> None:
|
||||
connectable = async_engine_from_config(
|
||||
config.get_section(config.config_ini_section, {}),
|
||||
poolclass=pool.NullPool,
|
||||
)
|
||||
connection = connectable.connect()
|
||||
|
||||
try:
|
||||
await connection.start()
|
||||
await connection.run_sync(do_run_migrations)
|
||||
finally:
|
||||
await connection.close()
|
||||
|
||||
await connectable.dispose()
|
||||
|
||||
|
||||
if context.is_offline_mode():
|
||||
run_migrations_offline()
|
||||
else:
|
||||
external_connection = config.attributes.get("connection")
|
||||
|
||||
if external_connection is not None:
|
||||
do_run_migrations(external_connection)
|
||||
else:
|
||||
asyncio.run(run_async_migrations())
|
||||
@@ -0,0 +1,24 @@
|
||||
"""${message}
|
||||
|
||||
Revision ID: ${up_revision}
|
||||
Revises: ${down_revision | comma,n}
|
||||
Create Date: ${create_date}
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
${imports if imports else ""}
|
||||
|
||||
revision: str = ${repr(up_revision)}
|
||||
down_revision: Union[str, None] = ${repr(down_revision)}
|
||||
branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
|
||||
depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
${upgrades if upgrades else "pass"}
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
${downgrades if downgrades else "pass"}
|
||||
@@ -0,0 +1,515 @@
|
||||
"""Начальная схема Телеграм-магазина
|
||||
|
||||
Revision ID: 0001_shop_schema
|
||||
Revises:
|
||||
Create Date: 2026-05-29 00:00:00
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
revision: str = "0001_shop_schema"
|
||||
down_revision: Union[str, None] = None
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
# Проверка наличия таблицы
|
||||
def _table_exists(table_name: str) -> bool:
|
||||
bind = op.get_bind()
|
||||
result = bind.execute(
|
||||
sa.text(
|
||||
"""
|
||||
SELECT name
|
||||
FROM sqlite_master
|
||||
WHERE type = 'table'
|
||||
AND name = :table_name
|
||||
"""
|
||||
),
|
||||
{"table_name": table_name},
|
||||
)
|
||||
|
||||
return result.scalar_one_or_none() is not None
|
||||
|
||||
|
||||
# Проверка наличия колонки
|
||||
def _column_exists(table_name: str, column_name: str) -> bool:
|
||||
bind = op.get_bind()
|
||||
result = bind.execute(sa.text(f"PRAGMA table_info({table_name})"))
|
||||
|
||||
return column_name in [row.name for row in result]
|
||||
|
||||
|
||||
# Добавление колонки, если ее нет
|
||||
def _add_column(table_name: str, column_name: str, column_sql: str) -> None:
|
||||
if not _column_exists(table_name, column_name):
|
||||
op.execute(sa.text(f"ALTER TABLE {table_name} ADD COLUMN {column_name} {column_sql}"))
|
||||
|
||||
|
||||
# Создание индекса, если его нет
|
||||
def _create_index(index_name: str, table_name: str, columns: str, *, unique: bool = False) -> None:
|
||||
unique_sql = "UNIQUE " if unique else ""
|
||||
op.execute(sa.text(f"CREATE {unique_sql}INDEX IF NOT EXISTS {index_name} ON {table_name} ({columns})"))
|
||||
|
||||
|
||||
# Нормализация текстовых bool-значений
|
||||
def _normalize_boolean_text(table_name: str, column_name: str, default_value: str) -> None:
|
||||
bind = op.get_bind()
|
||||
bind.execute(
|
||||
sa.text(
|
||||
f"""
|
||||
UPDATE {table_name}
|
||||
SET {column_name} = CASE LOWER(CAST(COALESCE({column_name}, :default_value) AS TEXT))
|
||||
WHEN '1' THEN 'True'
|
||||
WHEN 'true' THEN 'True'
|
||||
WHEN 'yes' THEN 'True'
|
||||
WHEN 'on' THEN 'True'
|
||||
WHEN 'да' THEN 'True'
|
||||
WHEN '0' THEN 'False'
|
||||
WHEN 'false' THEN 'False'
|
||||
WHEN 'no' THEN 'False'
|
||||
WHEN 'off' THEN 'False'
|
||||
WHEN 'нет' THEN 'False'
|
||||
ELSE :default_value
|
||||
END
|
||||
"""
|
||||
),
|
||||
{"default_value": default_value},
|
||||
)
|
||||
|
||||
|
||||
# Сбор SQL для переноса текстовой колонки
|
||||
def _copy_expr(table_name: str, column_name: str, default_value: str) -> str:
|
||||
if _column_exists(table_name, column_name):
|
||||
return f"COALESCE({column_name}, '{default_value}')"
|
||||
|
||||
return f"'{default_value}'"
|
||||
|
||||
|
||||
# Сбор SQL для переноса числовой колонки
|
||||
def _copy_int_expr(table_name: str, column_name: str, default_value: int = 0) -> str:
|
||||
if _column_exists(table_name, column_name):
|
||||
return f"COALESCE({column_name}, {default_value})"
|
||||
|
||||
return str(default_value)
|
||||
|
||||
|
||||
# Создание или правка таблицы пользователей
|
||||
def _ensure_users() -> None:
|
||||
bind = op.get_bind()
|
||||
|
||||
if not _table_exists("storage_users"):
|
||||
op.create_table(
|
||||
"storage_users",
|
||||
sa.Column("increment", sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column("user_id", sa.BigInteger(), nullable=False),
|
||||
sa.Column("user_login", sa.String(length=255), nullable=False, server_default=""),
|
||||
sa.Column("user_name", sa.String(length=255), nullable=False, server_default=""),
|
||||
sa.Column("user_surname", sa.String(length=255), nullable=False, server_default=""),
|
||||
sa.Column("user_fullname", sa.String(length=511), nullable=False, server_default=""),
|
||||
sa.Column("user_balance", sa.Float(), nullable=False, server_default="0"),
|
||||
sa.Column("user_refill", sa.Float(), nullable=False, server_default="0"),
|
||||
sa.Column("user_give", sa.Float(), nullable=False, server_default="0"),
|
||||
sa.Column("user_unix", sa.Integer(), nullable=False),
|
||||
sa.PrimaryKeyConstraint("increment"),
|
||||
)
|
||||
else:
|
||||
_add_column("storage_users", "user_login", "TEXT NOT NULL DEFAULT ''")
|
||||
_add_column("storage_users", "user_name", "TEXT NOT NULL DEFAULT ''")
|
||||
_add_column("storage_users", "user_surname", "TEXT NOT NULL DEFAULT ''")
|
||||
_add_column("storage_users", "user_fullname", "TEXT NOT NULL DEFAULT ''")
|
||||
_add_column("storage_users", "user_balance", "REAL NOT NULL DEFAULT 0")
|
||||
_add_column("storage_users", "user_refill", "REAL NOT NULL DEFAULT 0")
|
||||
_add_column("storage_users", "user_give", "REAL NOT NULL DEFAULT 0")
|
||||
_add_column("storage_users", "user_unix", "INTEGER NOT NULL DEFAULT 0")
|
||||
bind.execute(sa.text("UPDATE storage_users SET user_login = COALESCE(user_login, '')"))
|
||||
bind.execute(sa.text("UPDATE storage_users SET user_name = COALESCE(user_name, '')"))
|
||||
bind.execute(sa.text("UPDATE storage_users SET user_surname = COALESCE(user_surname, '')"))
|
||||
bind.execute(
|
||||
sa.text(
|
||||
"""
|
||||
UPDATE storage_users
|
||||
SET user_fullname = CASE
|
||||
WHEN user_fullname IS NULL OR user_fullname = '' THEN TRIM(user_name || ' ' || user_surname)
|
||||
ELSE user_fullname
|
||||
END
|
||||
"""
|
||||
)
|
||||
)
|
||||
bind.execute(sa.text("UPDATE storage_users SET user_balance = COALESCE(user_balance, 0)"))
|
||||
bind.execute(sa.text("UPDATE storage_users SET user_refill = COALESCE(user_refill, 0)"))
|
||||
bind.execute(sa.text("UPDATE storage_users SET user_give = COALESCE(user_give, 0)"))
|
||||
bind.execute(sa.text("UPDATE storage_users SET user_unix = COALESCE(user_unix, 0)"))
|
||||
bind.execute(sa.text("DELETE FROM storage_users WHERE user_id IS NULL"))
|
||||
bind.execute(
|
||||
sa.text(
|
||||
"""
|
||||
DELETE
|
||||
FROM storage_users
|
||||
WHERE user_id IS NOT NULL
|
||||
AND rowid NOT IN (
|
||||
SELECT MAX(rowid)
|
||||
FROM storage_users
|
||||
WHERE user_id IS NOT NULL
|
||||
GROUP BY user_id
|
||||
)
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
_create_index("ix_storage_users_user_id", "storage_users", "user_id", unique=True)
|
||||
|
||||
|
||||
# Создание или правка таблицы настроек
|
||||
def _ensure_settings() -> None:
|
||||
bind = op.get_bind()
|
||||
|
||||
if not _table_exists("storage_settings"):
|
||||
op.create_table(
|
||||
"storage_settings",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("status_work", sa.String(length=16), nullable=False, server_default="True"),
|
||||
sa.Column("status_refill", sa.String(length=16), nullable=False, server_default="False"),
|
||||
sa.Column("status_buy", sa.String(length=16), nullable=False, server_default="False"),
|
||||
sa.Column("notification_refill", sa.String(length=16), nullable=False, server_default="True"),
|
||||
sa.Column("notification_buy", sa.String(length=16), nullable=False, server_default="False"),
|
||||
sa.Column("misc_faq", sa.String(), nullable=False, server_default="None"),
|
||||
sa.Column("misc_support", sa.String(), nullable=False, server_default="None"),
|
||||
sa.Column("misc_bot", sa.String(length=255), nullable=False, server_default="None"),
|
||||
sa.Column("misc_hosting_text", sa.String(length=64), nullable=False, server_default="telegraph"),
|
||||
sa.Column("misc_token_telegraph", sa.String(), nullable=False, server_default="None"),
|
||||
sa.Column("misc_discord_webhook_url", sa.String(), nullable=False, server_default="None"),
|
||||
sa.Column("misc_discord_webhook_name", sa.String(length=255), nullable=False, server_default="None"),
|
||||
sa.Column("misc_hide_category", sa.String(length=16), nullable=False, server_default="False"),
|
||||
sa.Column("misc_hide_position", sa.String(length=16), nullable=False, server_default="False"),
|
||||
sa.Column("misc_profit_day", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("misc_profit_week", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("misc_profit_month", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
else:
|
||||
bind.execute(sa.text("DROP TABLE IF EXISTS storage_settings_new"))
|
||||
op.create_table(
|
||||
"storage_settings_new",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("status_work", sa.String(length=16), nullable=False, server_default="True"),
|
||||
sa.Column("status_refill", sa.String(length=16), nullable=False, server_default="False"),
|
||||
sa.Column("status_buy", sa.String(length=16), nullable=False, server_default="False"),
|
||||
sa.Column("notification_refill", sa.String(length=16), nullable=False, server_default="True"),
|
||||
sa.Column("notification_buy", sa.String(length=16), nullable=False, server_default="False"),
|
||||
sa.Column("misc_faq", sa.String(), nullable=False, server_default="None"),
|
||||
sa.Column("misc_support", sa.String(), nullable=False, server_default="None"),
|
||||
sa.Column("misc_bot", sa.String(length=255), nullable=False, server_default="None"),
|
||||
sa.Column("misc_hosting_text", sa.String(length=64), nullable=False, server_default="telegraph"),
|
||||
sa.Column("misc_token_telegraph", sa.String(), nullable=False, server_default="None"),
|
||||
sa.Column("misc_discord_webhook_url", sa.String(), nullable=False, server_default="None"),
|
||||
sa.Column("misc_discord_webhook_name", sa.String(length=255), nullable=False, server_default="None"),
|
||||
sa.Column("misc_hide_category", sa.String(length=16), nullable=False, server_default="False"),
|
||||
sa.Column("misc_hide_position", sa.String(length=16), nullable=False, server_default="False"),
|
||||
sa.Column("misc_profit_day", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("misc_profit_week", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("misc_profit_month", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
bind.execute(
|
||||
sa.text(
|
||||
f"""
|
||||
INSERT INTO storage_settings_new (
|
||||
id,
|
||||
status_work,
|
||||
status_refill,
|
||||
status_buy,
|
||||
notification_refill,
|
||||
notification_buy,
|
||||
misc_faq,
|
||||
misc_support,
|
||||
misc_bot,
|
||||
misc_hosting_text,
|
||||
misc_token_telegraph,
|
||||
misc_discord_webhook_url,
|
||||
misc_discord_webhook_name,
|
||||
misc_hide_category,
|
||||
misc_hide_position,
|
||||
misc_profit_day,
|
||||
misc_profit_week,
|
||||
misc_profit_month
|
||||
)
|
||||
SELECT
|
||||
1,
|
||||
{_copy_expr("storage_settings", "status_work", "True")},
|
||||
{_copy_expr("storage_settings", "status_refill", "False")},
|
||||
{_copy_expr("storage_settings", "status_buy", "False")},
|
||||
{_copy_expr("storage_settings", "notification_refill", "True")},
|
||||
{_copy_expr("storage_settings", "notification_buy", "False")},
|
||||
{_copy_expr("storage_settings", "misc_faq", "None")},
|
||||
{_copy_expr("storage_settings", "misc_support", "None")},
|
||||
{_copy_expr("storage_settings", "misc_bot", "None")},
|
||||
{_copy_expr("storage_settings", "misc_hosting_text", "telegraph")},
|
||||
{_copy_expr("storage_settings", "misc_token_telegraph", "None")},
|
||||
{_copy_expr("storage_settings", "misc_discord_webhook_url", "None")},
|
||||
{_copy_expr("storage_settings", "misc_discord_webhook_name", "None")},
|
||||
{_copy_expr("storage_settings", "misc_hide_category", "False")},
|
||||
{_copy_expr("storage_settings", "misc_hide_position", "False")},
|
||||
{_copy_int_expr("storage_settings", "misc_profit_day")},
|
||||
{_copy_int_expr("storage_settings", "misc_profit_week")},
|
||||
{_copy_int_expr("storage_settings", "misc_profit_month")}
|
||||
FROM storage_settings
|
||||
LIMIT 1
|
||||
"""
|
||||
)
|
||||
)
|
||||
bind.execute(sa.text("DROP TABLE storage_settings"))
|
||||
bind.execute(sa.text("ALTER TABLE storage_settings_new RENAME TO storage_settings"))
|
||||
|
||||
bind.execute(
|
||||
sa.text(
|
||||
"""
|
||||
INSERT INTO storage_settings (id)
|
||||
SELECT 1
|
||||
WHERE NOT EXISTS (SELECT 1 FROM storage_settings WHERE id = 1)
|
||||
"""
|
||||
)
|
||||
)
|
||||
for column, default in (
|
||||
("status_work", "True"),
|
||||
("status_refill", "False"),
|
||||
("status_buy", "False"),
|
||||
("notification_refill", "True"),
|
||||
("notification_buy", "False"),
|
||||
("misc_hide_category", "False"),
|
||||
("misc_hide_position", "False"),
|
||||
):
|
||||
_normalize_boolean_text("storage_settings", column, default)
|
||||
|
||||
_create_index("ix_storage_settings_id", "storage_settings", "id", unique=True)
|
||||
|
||||
|
||||
# Создание или правка таблицы платежей
|
||||
def _ensure_payments() -> None:
|
||||
bind = op.get_bind()
|
||||
|
||||
if not _table_exists("storage_payments"):
|
||||
op.create_table(
|
||||
"storage_payments",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("cryptobot_token", sa.String(), nullable=False, server_default="None"),
|
||||
sa.Column("yoomoney_token", sa.String(), nullable=False, server_default="None"),
|
||||
sa.Column("status_cryptobot", sa.String(length=16), nullable=False, server_default="False"),
|
||||
sa.Column("status_yoomoney", sa.String(length=16), nullable=False, server_default="False"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
else:
|
||||
bind.execute(sa.text("DROP TABLE IF EXISTS storage_payments_new"))
|
||||
op.create_table(
|
||||
"storage_payments_new",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("cryptobot_token", sa.String(), nullable=False, server_default="None"),
|
||||
sa.Column("yoomoney_token", sa.String(), nullable=False, server_default="None"),
|
||||
sa.Column("status_cryptobot", sa.String(length=16), nullable=False, server_default="False"),
|
||||
sa.Column("status_yoomoney", sa.String(length=16), nullable=False, server_default="False"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
bind.execute(
|
||||
sa.text(
|
||||
f"""
|
||||
INSERT INTO storage_payments_new (
|
||||
id,
|
||||
cryptobot_token,
|
||||
yoomoney_token,
|
||||
status_cryptobot,
|
||||
status_yoomoney
|
||||
)
|
||||
SELECT
|
||||
1,
|
||||
{_copy_expr("storage_payments", "cryptobot_token", "None")},
|
||||
{_copy_expr("storage_payments", "yoomoney_token", "None")},
|
||||
{_copy_expr("storage_payments", "status_cryptobot", "False")},
|
||||
{_copy_expr("storage_payments", "status_yoomoney", "False")}
|
||||
FROM storage_payments
|
||||
LIMIT 1
|
||||
"""
|
||||
)
|
||||
)
|
||||
bind.execute(sa.text("DROP TABLE storage_payments"))
|
||||
bind.execute(sa.text("ALTER TABLE storage_payments_new RENAME TO storage_payments"))
|
||||
|
||||
bind.execute(
|
||||
sa.text(
|
||||
"""
|
||||
INSERT INTO storage_payments (id)
|
||||
SELECT 1
|
||||
WHERE NOT EXISTS (SELECT 1 FROM storage_payments WHERE id = 1)
|
||||
"""
|
||||
)
|
||||
)
|
||||
_normalize_boolean_text("storage_payments", "status_cryptobot", "False")
|
||||
_normalize_boolean_text("storage_payments", "status_yoomoney", "False")
|
||||
_create_index("ix_storage_payments_id", "storage_payments", "id", unique=True)
|
||||
|
||||
|
||||
# Создание или правка таблицы категорий
|
||||
def _ensure_category() -> None:
|
||||
if not _table_exists("storage_category"):
|
||||
op.create_table(
|
||||
"storage_category",
|
||||
sa.Column("increment", sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column("category_id", sa.BigInteger(), nullable=False),
|
||||
sa.Column("category_name", sa.String(length=255), nullable=False),
|
||||
sa.Column("category_unix", sa.Integer(), nullable=False),
|
||||
sa.PrimaryKeyConstraint("increment"),
|
||||
)
|
||||
else:
|
||||
_add_column("storage_category", "category_id", "INTEGER NOT NULL DEFAULT 0")
|
||||
_add_column("storage_category", "category_name", "TEXT NOT NULL DEFAULT ''")
|
||||
_add_column("storage_category", "category_unix", "INTEGER NOT NULL DEFAULT 0")
|
||||
|
||||
_create_index("ix_storage_category_category_id", "storage_category", "category_id")
|
||||
|
||||
|
||||
# Создание или правка таблицы позиций
|
||||
def _ensure_position() -> None:
|
||||
if not _table_exists("storage_position"):
|
||||
op.create_table(
|
||||
"storage_position",
|
||||
sa.Column("increment", sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column("category_id", sa.BigInteger(), nullable=False),
|
||||
sa.Column("position_id", sa.BigInteger(), nullable=False),
|
||||
sa.Column("position_name", sa.String(length=255), nullable=False),
|
||||
sa.Column("position_price", sa.Float(), nullable=False, server_default="0"),
|
||||
sa.Column("position_desc", sa.Text(), nullable=False, server_default="None"),
|
||||
sa.Column("position_photo", sa.Text(), nullable=False, server_default="None"),
|
||||
sa.Column("position_unix", sa.Integer(), nullable=False),
|
||||
sa.PrimaryKeyConstraint("increment"),
|
||||
)
|
||||
else:
|
||||
_add_column("storage_position", "category_id", "INTEGER NOT NULL DEFAULT 0")
|
||||
_add_column("storage_position", "position_id", "INTEGER NOT NULL DEFAULT 0")
|
||||
_add_column("storage_position", "position_name", "TEXT NOT NULL DEFAULT ''")
|
||||
_add_column("storage_position", "position_price", "REAL NOT NULL DEFAULT 0")
|
||||
_add_column("storage_position", "position_desc", "TEXT NOT NULL DEFAULT 'None'")
|
||||
_add_column("storage_position", "position_photo", "TEXT NOT NULL DEFAULT 'None'")
|
||||
_add_column("storage_position", "position_unix", "INTEGER NOT NULL DEFAULT 0")
|
||||
|
||||
_create_index("ix_storage_position_category_id", "storage_position", "category_id")
|
||||
_create_index("ix_storage_position_position_id", "storage_position", "position_id")
|
||||
|
||||
|
||||
# Создание или правка таблицы товаров
|
||||
def _ensure_item() -> None:
|
||||
if not _table_exists("storage_item"):
|
||||
op.create_table(
|
||||
"storage_item",
|
||||
sa.Column("increment", sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column("user_id", sa.BigInteger(), nullable=False),
|
||||
sa.Column("category_id", sa.BigInteger(), nullable=False),
|
||||
sa.Column("position_id", sa.BigInteger(), nullable=False),
|
||||
sa.Column("item_id", sa.BigInteger(), nullable=False),
|
||||
sa.Column("item_unix", sa.Integer(), nullable=False),
|
||||
sa.Column("item_data", sa.Text(), nullable=False),
|
||||
sa.PrimaryKeyConstraint("increment"),
|
||||
)
|
||||
else:
|
||||
_add_column("storage_item", "user_id", "INTEGER NOT NULL DEFAULT 0")
|
||||
_add_column("storage_item", "category_id", "INTEGER NOT NULL DEFAULT 0")
|
||||
_add_column("storage_item", "position_id", "INTEGER NOT NULL DEFAULT 0")
|
||||
_add_column("storage_item", "item_id", "INTEGER NOT NULL DEFAULT 0")
|
||||
_add_column("storage_item", "item_unix", "INTEGER NOT NULL DEFAULT 0")
|
||||
_add_column("storage_item", "item_data", "TEXT NOT NULL DEFAULT ''")
|
||||
|
||||
_create_index("ix_storage_item_user_id", "storage_item", "user_id")
|
||||
_create_index("ix_storage_item_category_id", "storage_item", "category_id")
|
||||
_create_index("ix_storage_item_position_id", "storage_item", "position_id")
|
||||
_create_index("ix_storage_item_item_id", "storage_item", "item_id")
|
||||
|
||||
|
||||
# Создание или правка таблицы покупок
|
||||
def _ensure_purchases() -> None:
|
||||
if not _table_exists("storage_purchases"):
|
||||
op.create_table(
|
||||
"storage_purchases",
|
||||
sa.Column("increment", sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column("user_id", sa.BigInteger(), nullable=False),
|
||||
sa.Column("user_balance_before", sa.Float(), nullable=False, server_default="0"),
|
||||
sa.Column("user_balance_after", sa.Float(), nullable=False, server_default="0"),
|
||||
sa.Column("purchase_receipt", sa.BigInteger(), nullable=False),
|
||||
sa.Column("purchase_data", sa.Text(), nullable=False),
|
||||
sa.Column("purchase_count", sa.Integer(), nullable=False, server_default="1"),
|
||||
sa.Column("purchase_price", sa.Float(), nullable=False, server_default="0"),
|
||||
sa.Column("purchase_price_one", sa.Float(), nullable=False, server_default="0"),
|
||||
sa.Column("purchase_position_id", sa.BigInteger(), nullable=False),
|
||||
sa.Column("purchase_position_name", sa.String(length=255), nullable=False),
|
||||
sa.Column("purchase_category_id", sa.BigInteger(), nullable=False),
|
||||
sa.Column("purchase_category_name", sa.String(length=255), nullable=False),
|
||||
sa.Column("purchase_unix", sa.Integer(), nullable=False),
|
||||
sa.PrimaryKeyConstraint("increment"),
|
||||
)
|
||||
else:
|
||||
_add_column("storage_purchases", "user_id", "INTEGER NOT NULL DEFAULT 0")
|
||||
_add_column("storage_purchases", "user_balance_before", "REAL NOT NULL DEFAULT 0")
|
||||
_add_column("storage_purchases", "user_balance_after", "REAL NOT NULL DEFAULT 0")
|
||||
_add_column("storage_purchases", "purchase_receipt", "INTEGER NOT NULL DEFAULT 0")
|
||||
_add_column("storage_purchases", "purchase_data", "TEXT NOT NULL DEFAULT ''")
|
||||
_add_column("storage_purchases", "purchase_count", "INTEGER NOT NULL DEFAULT 1")
|
||||
_add_column("storage_purchases", "purchase_price", "REAL NOT NULL DEFAULT 0")
|
||||
_add_column("storage_purchases", "purchase_price_one", "REAL NOT NULL DEFAULT 0")
|
||||
_add_column("storage_purchases", "purchase_position_id", "INTEGER NOT NULL DEFAULT 0")
|
||||
_add_column("storage_purchases", "purchase_position_name", "TEXT NOT NULL DEFAULT ''")
|
||||
_add_column("storage_purchases", "purchase_category_id", "INTEGER NOT NULL DEFAULT 0")
|
||||
_add_column("storage_purchases", "purchase_category_name", "TEXT NOT NULL DEFAULT ''")
|
||||
_add_column("storage_purchases", "purchase_unix", "INTEGER NOT NULL DEFAULT 0")
|
||||
|
||||
_create_index("ix_storage_purchases_user_id", "storage_purchases", "user_id")
|
||||
_create_index("ix_storage_purchases_purchase_receipt", "storage_purchases", "purchase_receipt")
|
||||
_create_index("ix_storage_purchases_purchase_position_id", "storage_purchases", "purchase_position_id")
|
||||
_create_index("ix_storage_purchases_purchase_category_id", "storage_purchases", "purchase_category_id")
|
||||
|
||||
|
||||
# Создание или правка таблицы пополнений
|
||||
def _ensure_refill() -> None:
|
||||
if not _table_exists("storage_refill"):
|
||||
op.create_table(
|
||||
"storage_refill",
|
||||
sa.Column("increment", sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column("user_id", sa.BigInteger(), nullable=False),
|
||||
sa.Column("refill_receipt", sa.BigInteger(), nullable=False),
|
||||
sa.Column("refill_comment", sa.String(length=255), nullable=False, server_default=""),
|
||||
sa.Column("refill_amount", sa.Float(), nullable=False, server_default="0"),
|
||||
sa.Column("refill_method", sa.String(length=64), nullable=False),
|
||||
sa.Column("refill_unix", sa.Integer(), nullable=False),
|
||||
sa.PrimaryKeyConstraint("increment"),
|
||||
)
|
||||
else:
|
||||
_add_column("storage_refill", "user_id", "INTEGER NOT NULL DEFAULT 0")
|
||||
_add_column("storage_refill", "refill_receipt", "INTEGER NOT NULL DEFAULT 0")
|
||||
_add_column("storage_refill", "refill_comment", "TEXT NOT NULL DEFAULT ''")
|
||||
_add_column("storage_refill", "refill_amount", "REAL NOT NULL DEFAULT 0")
|
||||
_add_column("storage_refill", "refill_method", "TEXT NOT NULL DEFAULT ''")
|
||||
_add_column("storage_refill", "refill_unix", "INTEGER NOT NULL DEFAULT 0")
|
||||
|
||||
_create_index("ix_storage_refill_user_id", "storage_refill", "user_id")
|
||||
_create_index("ix_storage_refill_refill_receipt", "storage_refill", "refill_receipt")
|
||||
|
||||
|
||||
# Применение начальной схемы магазина
|
||||
def upgrade() -> None:
|
||||
_ensure_users()
|
||||
_ensure_settings()
|
||||
_ensure_payments()
|
||||
_ensure_category()
|
||||
_ensure_position()
|
||||
_ensure_item()
|
||||
_ensure_purchases()
|
||||
_ensure_refill()
|
||||
|
||||
|
||||
# Откат начальной схемы магазина
|
||||
def downgrade() -> None:
|
||||
op.drop_table("storage_refill")
|
||||
op.drop_table("storage_purchases")
|
||||
op.drop_table("storage_item")
|
||||
op.drop_table("storage_position")
|
||||
op.drop_table("storage_category")
|
||||
op.drop_table("storage_payments")
|
||||
op.drop_table("storage_settings")
|
||||
op.drop_table("storage_users")
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
"""add column 'status_stars' into storage_payments
|
||||
|
||||
Revision ID: 492769059249
|
||||
Revises: 0001_shop_schema
|
||||
Create Date: 2026-05-30 19:15:00.214301+03:00
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
revision: str = '492769059249'
|
||||
down_revision: Union[str, None] = '0001_shop_schema'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('storage_payments', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('status_stars', sa.String(length=16), server_default=sa.text("'False'"), nullable=False))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('storage_payments', schema=None) as batch_op:
|
||||
batch_op.drop_column('status_stars')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
"""add column 'stars_course' into storage_payment
|
||||
|
||||
Revision ID: 700275128e27
|
||||
Revises: 492769059249
|
||||
Create Date: 2026-05-30 19:28:42.490458+03:00
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
revision: str = '700275128e27'
|
||||
down_revision: Union[str, None] = '492769059249'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('storage_payments', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('stars_course', sa.Float(), server_default=sa.text('(1.5)'), nullable=False))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('storage_payments', schema=None) as batch_op:
|
||||
batch_op.drop_column('stars_course')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,32 @@
|
||||
"""add column 'misc_method_prod' into 'storage_settings'
|
||||
|
||||
Revision ID: 36380377b0e6
|
||||
Revises: 700275128e27
|
||||
Create Date: 2026-06-01 19:34:35.858117+03:00
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
revision: str = '36380377b0e6'
|
||||
down_revision: Union[str, None] = '700275128e27'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('storage_settings', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('misc_method_prod', sa.String(length=16), server_default=sa.text("'skip'"), nullable=False))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('storage_settings', schema=None) as batch_op:
|
||||
batch_op.drop_column('misc_method_prod')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
Reference in New Issue
Block a user