"""Seed data for VoIdeaAI. Called from app lifespan when DB tables are empty. Creates default tariff plans and sets system owner by email. """ import json from pathlib import Path from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.core.config import get_settings from app.models.agent import AgentConfig from app.models.tariff import TariffPlan from app.services.user_service import UserService settings = get_settings() _AGENT_VERSIONS_FILE = Path(__file__).resolve().parent.parent.parent / "AGENT_VERSIONS.json" def _load_agent_versions() -> dict[str, str]: try: return json.loads(_AGENT_VERSIONS_FILE.read_text(encoding="utf-8")) except (FileNotFoundError, json.JSONDecodeError, OSError): return {} AGENT_DESCRIPTIONS: dict[str, str] = { "conductor": "Дирижёр — оркестратор, единственная точка входа. Маршрутизирует запросы к ролевым агентам, верифицирует ответы.", "business_analyst": "Бизнес-аналитик — оценивает бизнес-метрики, ROI, конкурентную среду, окупаемость идеи.", "task_organizer": "Организатор задач — разбивает идею на конкретные шаги реализации с оценкой сроков.", "lawyer": "Юрист — проверяет идею на соответствие законодательству РФ (44-ФЗ, 152-ФЗ, 223-ФЗ).", "financial_consultant": "Финансовый консультант — бюджет, прогноз выручки, точка безубыточности.", "solution_architect": "Архитектор решений — предлагает 2 варианта архитектуры: монолит и микросервисы.", "tester": "Тестировщик — описывает сценарии тестирования (позитивные и негативные кейсы).", "ui_designer": "UI-дизайнер — предлагает 2 варианта визуального дизайна интерфейса.", "smm_specialist": "SMM-специалист — разрабатывает контент-план для соцсетей.", "life_coach": "Лайф-коуч — ставит SMART-цели, поквартальные вехи развития.", "accessibility_expert": "Эксперт по доступности — проверяет на WCAG 2.1 AA соответствие.", "critic": "Критик — конструктивная критика, поиск «слепых зон» идеи.", "copywriter": "Копирайтер — упаковывает идею в продающий текст для разных аудиторий.", "keeper": "Хранитель — сохраняет проработанную идею в базу данных.", "doc_agent": "DocAgent — автоматическая генерация и обновление документации.", "backlog_agent": "BacklogAgent — управление бэклогом, приоритизация задач.", "spec_agent": "SpecAgent — написание формальных спецификаций и требований.", "audit_agent": "AuditAgent — аудит кода, архитектуры, безопасности.", "observer_agent": "ObserverAgent — мониторинг системы, сбор метрик, алерты.", "evolution_agent": "EvolutionAgent — автоматическое улучшение кода, рефакторинг.", "security_agent": "SecurityAgent — проверка безопасности, уязвимости, OWASP.", "qa_tester_agent": "QATesterAgent — автоматизированное тестирование, регресс.", "fix_agent": "FixAgent — автоматическое исправление ошибок на основе логов.", "ui_test_agent": "UITestAgent — тестирование UI/UX, скриншотные тесты.", "rollout_agent": "RolloutAgent — управление релизами, миграции, откаты.", } async def seed_database(db: AsyncSession) -> None: """Seed initial data if tables are empty.""" await _seed_tariff_plans(db) await _seed_agent_descriptions(db) await _seed_owner(db) async def _seed_tariff_plans(db: AsyncSession) -> None: """Create default Free tariff plan if none exist.""" result = await db.execute(select(TariffPlan).limit(1)) if result.scalar_one_or_none(): return free_plan = TariffPlan( name="Бесплатно", code="free", description="Базовый доступ ко всем функциям. Сейчас всё бесплатно 🎉", price_monthly=0, features={ "limit_ideas": None, "limit_sessions_per_day": None, "limit_agents": None, "limit_team_members": None, "limit_storage_mb": 100, "has_voice": True, "has_voice_commands": True, "has_drive": True, "has_advanced_analytics": True, "has_api_access": True, "has_priority_support": True, "has_custom_branding": True, "_promo": True, }, is_active=True, sort_order=0, ) db.add(free_plan) await db.commit() async def _seed_agent_descriptions(db: AsyncSession) -> None: """Fill empty agent descriptions and update versions from AGENT_VERSIONS.json.""" agent_versions = _load_agent_versions() result = await db.execute(select(AgentConfig)) agents = result.scalars().all() updated = False for agent in agents: name = agent.agent_name.lower() if name in AGENT_DESCRIPTIONS and (not agent.description or agent.description == ""): agent.description = AGENT_DESCRIPTIONS[name] updated = True name_lower = agent.agent_name.lower() expected_version = agent_versions.get(name_lower) or agent_versions.get(agent.agent_name) if expected_version and agent.version != expected_version: agent.version = expected_version updated = True if updated: await db.commit() async def _seed_owner(db: AsyncSession) -> None: """Set system owner by SYSTEM_OWNER_EMAIL if configured.""" if not settings.system_owner_email: return svc = UserService(db) await svc.set_owner_by_email(settings.system_owner_email)