feat: JLPT levels for Japanese, custom practice scenarios, UI improvements
- Add separate level systems: CEFR (A1-C2) for European languages, JLPT (N5-N1) for Japanese - Store levels per language in new `levels_by_language` JSON field - Add custom scenario option in AI practice mode - Show action buttons after practice ends (new dialogue, tasks, words) - Fix level display across all handlers to use correct level system - Add Alembic migration for levels_by_language field - Update all locale files (ru, en, ja) with new keys 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
98
utils/levels.py
Normal file
98
utils/levels.py
Normal file
@@ -0,0 +1,98 @@
|
||||
"""
|
||||
Утилиты для работы с уровнями языка (CEFR и JLPT)
|
||||
"""
|
||||
from database.models import JLPT_LANGUAGES, DEFAULT_CEFR_LEVEL, DEFAULT_JLPT_LEVEL
|
||||
|
||||
|
||||
# Все доступные уровни по системам
|
||||
CEFR_LEVELS = ["A1", "A2", "B1", "B2", "C1", "C2"]
|
||||
JLPT_LEVELS = ["N5", "N4", "N3", "N2", "N1"]
|
||||
|
||||
|
||||
def get_level_system(learning_language: str) -> str:
|
||||
"""Определить систему уровней для языка"""
|
||||
return "jlpt" if learning_language in JLPT_LANGUAGES else "cefr"
|
||||
|
||||
|
||||
def get_available_levels(learning_language: str) -> list[str]:
|
||||
"""Получить список доступных уровней для языка"""
|
||||
if learning_language in JLPT_LANGUAGES:
|
||||
return JLPT_LEVELS
|
||||
return CEFR_LEVELS
|
||||
|
||||
|
||||
def get_default_level(learning_language: str) -> str:
|
||||
"""Получить дефолтный уровень для языка"""
|
||||
if learning_language in JLPT_LANGUAGES:
|
||||
return DEFAULT_JLPT_LEVEL
|
||||
return DEFAULT_CEFR_LEVEL
|
||||
|
||||
|
||||
def get_user_level_for_language(user, language: str = None) -> str:
|
||||
"""
|
||||
Получить уровень пользователя для конкретного языка.
|
||||
|
||||
Args:
|
||||
user: Объект пользователя
|
||||
language: Код языка (если None, берётся learning_language пользователя)
|
||||
|
||||
Returns:
|
||||
Строка уровня (например "B1" или "N4")
|
||||
"""
|
||||
if language is None:
|
||||
language = user.learning_language or "en"
|
||||
|
||||
# Пытаемся получить из JSON поля
|
||||
levels = user.levels_by_language or {}
|
||||
if language in levels:
|
||||
return levels[language]
|
||||
|
||||
# Fallback на старое поле level (для CEFR языков)
|
||||
if language not in JLPT_LANGUAGES and user.level:
|
||||
return user.level.value
|
||||
|
||||
# Возвращаем дефолт
|
||||
return get_default_level(language)
|
||||
|
||||
|
||||
def set_user_level_for_language(user, level: str, language: str = None) -> dict:
|
||||
"""
|
||||
Установить уровень пользователя для конкретного языка.
|
||||
|
||||
Args:
|
||||
user: Объект пользователя
|
||||
level: Уровень (например "B1" или "N4")
|
||||
language: Код языка (если None, берётся learning_language пользователя)
|
||||
|
||||
Returns:
|
||||
Обновлённый словарь levels_by_language
|
||||
"""
|
||||
if language is None:
|
||||
language = user.learning_language or "en"
|
||||
|
||||
# Инициализируем JSON если его нет
|
||||
if user.levels_by_language is None:
|
||||
user.levels_by_language = {}
|
||||
|
||||
# Копируем для изменения (SQLAlchemy требует новый объект для JSON)
|
||||
levels = dict(user.levels_by_language)
|
||||
levels[language] = level
|
||||
user.levels_by_language = levels
|
||||
|
||||
return levels
|
||||
|
||||
|
||||
def get_level_key_for_i18n(learning_language: str, level: str) -> str:
|
||||
"""
|
||||
Получить ключ локализации для уровня.
|
||||
|
||||
Args:
|
||||
learning_language: Язык изучения
|
||||
level: Уровень
|
||||
|
||||
Returns:
|
||||
Ключ для функции t() (например "settings.level.b1" или "settings.jlpt.n4")
|
||||
"""
|
||||
if learning_language in JLPT_LANGUAGES:
|
||||
return f"settings.jlpt.{level.lower()}"
|
||||
return f"settings.level.{level.lower()}"
|
||||
Reference in New Issue
Block a user