"""Seed shop items (frames, titles, consumables) Revision ID: 024_seed_shop_items Revises: 023_add_shop_system Create Date: 2025-01-05 """ from typing import Sequence, Union from datetime import datetime from alembic import op import sqlalchemy as sa from sqlalchemy import inspect # revision identifiers, used by Alembic. revision: str = '024_seed_shop_items' down_revision: Union[str, None] = '023_add_shop_system' 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() inspector = inspect(bind) return table_name in inspector.get_table_names() def upgrade() -> None: if not table_exists('shop_items'): return now = datetime.utcnow() # Таблица shop_items shop_items = sa.table( 'shop_items', sa.column('id', sa.Integer), sa.column('item_type', sa.String), sa.column('code', sa.String), sa.column('name', sa.String), sa.column('description', sa.Text), sa.column('price', sa.Integer), sa.column('rarity', sa.String), sa.column('asset_data', sa.JSON), sa.column('is_active', sa.Boolean), sa.column('created_at', sa.DateTime), ) # === Рамки аватара === frames = [ { 'item_type': 'frame', 'code': 'frame_bronze', 'name': 'Бронзовая рамка', 'description': 'Простая бронзовая рамка для начинающих', 'price': 50, 'rarity': 'common', 'asset_data': { 'border_color': '#CD7F32', 'border_width': 3, 'border_style': 'solid' }, 'is_active': True, }, { 'item_type': 'frame', 'code': 'frame_silver', 'name': 'Серебряная рамка', 'description': 'Элегантная серебряная рамка', 'price': 100, 'rarity': 'uncommon', 'asset_data': { 'border_color': '#C0C0C0', 'border_width': 3, 'border_style': 'solid' }, 'is_active': True, }, { 'item_type': 'frame', 'code': 'frame_gold', 'name': 'Золотая рамка', 'description': 'Престижная золотая рамка', 'price': 200, 'rarity': 'rare', 'asset_data': { 'border_color': '#FFD700', 'border_width': 4, 'border_style': 'solid' }, 'is_active': True, }, { 'item_type': 'frame', 'code': 'frame_diamond', 'name': 'Бриллиантовая рамка', 'description': 'Сверкающая бриллиантовая рамка для истинных ценителей', 'price': 500, 'rarity': 'epic', 'asset_data': { 'border_color': '#B9F2FF', 'border_width': 4, 'border_style': 'double', 'glow': True, 'glow_color': '#B9F2FF' }, 'is_active': True, }, { 'item_type': 'frame', 'code': 'frame_fire', 'name': 'Огненная рамка', 'description': 'Анимированная рамка с эффектом пламени', 'price': 1000, 'rarity': 'legendary', 'asset_data': { 'border_style': 'gradient', 'gradient': ['#FF4500', '#FF8C00', '#FFD700'], 'animated': True, 'animation': 'fire-pulse' }, 'is_active': True, }, { 'item_type': 'frame', 'code': 'frame_neon', 'name': 'Неоновая рамка', 'description': 'Яркая неоновая рамка с свечением', 'price': 800, 'rarity': 'epic', 'asset_data': { 'border_color': '#00FF00', 'border_width': 3, 'glow': True, 'glow_color': '#00FF00', 'glow_intensity': 10 }, 'is_active': True, }, { 'item_type': 'frame', 'code': 'frame_rainbow', 'name': 'Радужная рамка', 'description': 'Переливающаяся радужная рамка', 'price': 1500, 'rarity': 'legendary', 'asset_data': { 'border_style': 'gradient', 'gradient': ['#FF0000', '#FF7F00', '#FFFF00', '#00FF00', '#0000FF', '#4B0082', '#9400D3'], 'animated': True, 'animation': 'rainbow-rotate' }, 'is_active': True, }, ] # === Титулы === titles = [ { 'item_type': 'title', 'code': 'title_newcomer', 'name': 'Новичок', 'description': 'Первый шаг в мир марафонов', 'price': 30, 'rarity': 'common', 'asset_data': { 'text': 'Новичок', 'color': '#808080' }, 'is_active': True, }, { 'item_type': 'title', 'code': 'title_runner', 'name': 'Марафонец', 'description': 'Опытный участник марафонов', 'price': 100, 'rarity': 'uncommon', 'asset_data': { 'text': 'Марафонец', 'color': '#4169E1' }, 'is_active': True, }, { 'item_type': 'title', 'code': 'title_hunter', 'name': 'Охотник за челленджами', 'description': 'Мастер выполнения сложных заданий', 'price': 200, 'rarity': 'rare', 'asset_data': { 'text': 'Охотник за челленджами', 'color': '#228B22' }, 'is_active': True, }, { 'item_type': 'title', 'code': 'title_veteran', 'name': 'Ветеран', 'description': 'Закаленный в боях участник', 'price': 300, 'rarity': 'rare', 'asset_data': { 'text': 'Ветеран', 'color': '#8B4513' }, 'is_active': True, }, { 'item_type': 'title', 'code': 'title_champion', 'name': 'Чемпион', 'description': 'Победитель марафонов', 'price': 500, 'rarity': 'epic', 'asset_data': { 'text': 'Чемпион', 'color': '#FFD700', 'icon': 'trophy' }, 'is_active': True, }, { 'item_type': 'title', 'code': 'title_legend', 'name': 'Легенда', 'description': 'Легендарный участник марафонов', 'price': 1000, 'rarity': 'legendary', 'asset_data': { 'text': 'Легенда', 'color': '#FF4500', 'glow': True, 'icon': 'star' }, 'is_active': True, }, ] # === Цвета никнейма === name_colors = [ { 'item_type': 'name_color', 'code': 'color_red', 'name': 'Красный ник', 'description': 'Яркий красный цвет никнейма', 'price': 150, 'rarity': 'uncommon', 'asset_data': { 'style': 'solid', 'color': '#FF4444' }, 'is_active': True, }, { 'item_type': 'name_color', 'code': 'color_blue', 'name': 'Синий ник', 'description': 'Глубокий синий цвет никнейма', 'price': 150, 'rarity': 'uncommon', 'asset_data': { 'style': 'solid', 'color': '#4444FF' }, 'is_active': True, }, { 'item_type': 'name_color', 'code': 'color_green', 'name': 'Зеленый ник', 'description': 'Сочный зеленый цвет никнейма', 'price': 150, 'rarity': 'uncommon', 'asset_data': { 'style': 'solid', 'color': '#44FF44' }, 'is_active': True, }, { 'item_type': 'name_color', 'code': 'color_purple', 'name': 'Фиолетовый ник', 'description': 'Королевский фиолетовый цвет', 'price': 200, 'rarity': 'rare', 'asset_data': { 'style': 'solid', 'color': '#9932CC' }, 'is_active': True, }, { 'item_type': 'name_color', 'code': 'color_gold', 'name': 'Золотой ник', 'description': 'Престижный золотой цвет', 'price': 300, 'rarity': 'rare', 'asset_data': { 'style': 'solid', 'color': '#FFD700' }, 'is_active': True, }, { 'item_type': 'name_color', 'code': 'color_gradient_sunset', 'name': 'Закат', 'description': 'Красивый градиент заката', 'price': 500, 'rarity': 'epic', 'asset_data': { 'style': 'gradient', 'gradient': ['#FF6B6B', '#FFE66D'] }, 'is_active': True, }, { 'item_type': 'name_color', 'code': 'color_gradient_ocean', 'name': 'Океан', 'description': 'Градиент морских глубин', 'price': 500, 'rarity': 'epic', 'asset_data': { 'style': 'gradient', 'gradient': ['#4ECDC4', '#44A3FF'] }, 'is_active': True, }, { 'item_type': 'name_color', 'code': 'color_rainbow', 'name': 'Радужный ник', 'description': 'Анимированный радужный цвет', 'price': 1000, 'rarity': 'legendary', 'asset_data': { 'style': 'animated', 'animation': 'rainbow-shift' }, 'is_active': True, }, ] # === Фоны профиля === backgrounds = [ { 'item_type': 'background', 'code': 'bg_dark', 'name': 'Тёмный фон', 'description': 'Элегантный тёмный фон', 'price': 100, 'rarity': 'common', 'asset_data': { 'type': 'solid', 'color': '#1a1a2e' }, 'is_active': True, }, { 'item_type': 'background', 'code': 'bg_gradient_purple', 'name': 'Фиолетовый градиент', 'description': 'Красивый фиолетовый градиент', 'price': 200, 'rarity': 'uncommon', 'asset_data': { 'type': 'gradient', 'gradient': ['#1a1a2e', '#4a0080'] }, 'is_active': True, }, { 'item_type': 'background', 'code': 'bg_stars', 'name': 'Звёздное небо', 'description': 'Фон с мерцающими звёздами', 'price': 400, 'rarity': 'rare', 'asset_data': { 'type': 'pattern', 'pattern': 'stars', 'animated': True }, 'is_active': True, }, { 'item_type': 'background', 'code': 'bg_gaming', 'name': 'Игровой фон', 'description': 'Фон с игровыми элементами', 'price': 500, 'rarity': 'epic', 'asset_data': { 'type': 'pattern', 'pattern': 'gaming-icons' }, 'is_active': True, }, { 'item_type': 'background', 'code': 'bg_fire', 'name': 'Огненный фон', 'description': 'Анимированный огненный фон', 'price': 800, 'rarity': 'legendary', 'asset_data': { 'type': 'animated', 'animation': 'fire-particles' }, 'is_active': True, }, ] # === Расходуемые предметы === consumables = [ { 'item_type': 'consumable', 'code': 'skip', 'name': 'Пропуск', 'description': 'Пропустить текущее задание без штрафа и потери streak', 'price': 100, 'rarity': 'common', 'asset_data': { 'effect': 'skip', 'icon': 'skip-forward' }, 'is_active': True, }, { 'item_type': 'consumable', 'code': 'shield', 'name': 'Щит', 'description': 'Защита от штрафа при следующем дропе. Streak сохраняется.', 'price': 150, 'rarity': 'uncommon', 'asset_data': { 'effect': 'shield', 'icon': 'shield' }, 'is_active': True, }, { 'item_type': 'consumable', 'code': 'boost', 'name': 'Буст x1.5', 'description': 'Множитель очков x1.5 на следующие 2 часа', 'price': 200, 'rarity': 'rare', 'asset_data': { 'effect': 'boost', 'multiplier': 1.5, 'duration_hours': 2, 'icon': 'zap' }, 'is_active': True, }, { 'item_type': 'consumable', 'code': 'reroll', 'name': 'Перекрут', 'description': 'Перекрутить колесо и получить новое задание', 'price': 80, 'rarity': 'common', 'asset_data': { 'effect': 'reroll', 'icon': 'refresh-cw' }, 'is_active': True, }, ] # Вставляем все товары all_items = frames + titles + name_colors + backgrounds + consumables # Добавляем created_at ко всем товарам for item in all_items: item['created_at'] = now op.bulk_insert(shop_items, all_items) def downgrade() -> None: # Удаляем все seed-товары по коду op.execute("DELETE FROM shop_items WHERE code LIKE 'frame_%'") op.execute("DELETE FROM shop_items WHERE code LIKE 'title_%'") op.execute("DELETE FROM shop_items WHERE code LIKE 'color_%'") op.execute("DELETE FROM shop_items WHERE code LIKE 'bg_%'") op.execute("DELETE FROM shop_items WHERE code IN ('skip', 'shield', 'boost', 'reroll')")