feat: мини-игры, premium подписка, улучшенные контексты
Мини-игры (/games): - Speed Round: 10 раундов, 10 секунд на ответ, очки за скорость - Match Pairs: 5 слов + 5 переводов, соединить пары Premium-функции: - Поля is_premium и premium_until для пользователей - AI режим проверки ответов (учитывает синонимы) - Batch проверка всех ответов одним запросом Улучшения: - Примеры использования для всех добавляемых слов - Разбиение переводов по запятой на отдельные записи - Полные предложения в контекстах (без ___) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from database.models import Vocabulary, WordSource, LanguageLevel, WordTranslation
|
||||
from typing import List, Optional, Dict
|
||||
import random
|
||||
import re
|
||||
from typing import List, Optional, Dict
|
||||
|
||||
from sqlalchemy import select, func
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from database.models import Vocabulary, WordSource, LanguageLevel, WordTranslation
|
||||
|
||||
|
||||
class VocabularyService:
|
||||
@@ -263,6 +266,61 @@ class VocabularyService:
|
||||
|
||||
return new_translation
|
||||
|
||||
@staticmethod
|
||||
async def add_translation_split(
|
||||
session: AsyncSession,
|
||||
vocabulary_id: int,
|
||||
translation: str,
|
||||
context: Optional[str] = None,
|
||||
context_translation: Optional[str] = None,
|
||||
is_primary: bool = True
|
||||
) -> List[WordTranslation]:
|
||||
"""
|
||||
Добавить перевод(ы) к слову, разбивая строку по разделителям.
|
||||
|
||||
Если translation содержит несколько переводов через запятую или точку с запятой,
|
||||
каждый перевод добавляется отдельной записью.
|
||||
|
||||
Args:
|
||||
session: Сессия базы данных
|
||||
vocabulary_id: ID слова в словаре
|
||||
translation: Перевод (может содержать несколько через запятую)
|
||||
context: Пример использования на языке изучения
|
||||
context_translation: Перевод примера
|
||||
is_primary: Является ли первый перевод основным
|
||||
|
||||
Returns:
|
||||
Список созданных переводов
|
||||
"""
|
||||
import re
|
||||
|
||||
# Разбиваем по запятой или точке с запятой
|
||||
parts = re.split(r'[,;]\s*', translation)
|
||||
|
||||
# Очищаем и фильтруем пустые
|
||||
translations = [p.strip() for p in parts if p.strip()]
|
||||
|
||||
if not translations:
|
||||
return []
|
||||
|
||||
created = []
|
||||
for i, tr in enumerate(translations):
|
||||
new_translation = WordTranslation(
|
||||
vocabulary_id=vocabulary_id,
|
||||
translation=tr,
|
||||
context=context if i == 0 else None, # Контекст только для первого перевода
|
||||
context_translation=context_translation if i == 0 else None,
|
||||
is_primary=(is_primary and i == 0) # Только первый - основной
|
||||
)
|
||||
session.add(new_translation)
|
||||
created.append(new_translation)
|
||||
|
||||
await session.commit()
|
||||
for t in created:
|
||||
await session.refresh(t)
|
||||
|
||||
return created
|
||||
|
||||
@staticmethod
|
||||
async def add_translations_bulk(
|
||||
session: AsyncSession,
|
||||
@@ -368,3 +426,103 @@ class VocabularyService:
|
||||
await session.commit()
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
async def get_user_word_count(session: AsyncSession, user_id: int) -> int:
|
||||
"""
|
||||
Получить количество слов пользователя
|
||||
|
||||
Args:
|
||||
session: Сессия базы данных
|
||||
user_id: ID пользователя
|
||||
|
||||
Returns:
|
||||
Количество слов
|
||||
"""
|
||||
result = await session.execute(
|
||||
select(func.count(Vocabulary.id)).where(Vocabulary.user_id == user_id)
|
||||
)
|
||||
return result.scalar() or 0
|
||||
|
||||
@staticmethod
|
||||
async def get_random_words_for_game(
|
||||
session: AsyncSession,
|
||||
user_id: int,
|
||||
count: int = 10,
|
||||
learning_lang: Optional[str] = None
|
||||
) -> List[Vocabulary]:
|
||||
"""
|
||||
Получить случайные слова для мини-игры
|
||||
|
||||
Args:
|
||||
session: Сессия базы данных
|
||||
user_id: ID пользователя
|
||||
count: Количество слов
|
||||
learning_lang: Язык изучения для фильтрации
|
||||
|
||||
Returns:
|
||||
Список случайных слов
|
||||
"""
|
||||
result = await session.execute(
|
||||
select(Vocabulary).where(Vocabulary.user_id == user_id)
|
||||
)
|
||||
words = list(result.scalars().all())
|
||||
|
||||
if learning_lang:
|
||||
words = VocabularyService._filter_by_learning_lang(words, learning_lang)
|
||||
|
||||
# Перемешиваем и берём нужное количество
|
||||
random.shuffle(words)
|
||||
return words[:count]
|
||||
|
||||
@staticmethod
|
||||
async def get_random_words_with_translations(
|
||||
session: AsyncSession,
|
||||
user_id: int,
|
||||
count: int = 10,
|
||||
learning_lang: Optional[str] = None
|
||||
) -> List[dict]:
|
||||
"""
|
||||
Получить случайные слова для мини-игры вместе со всеми переводами
|
||||
|
||||
Args:
|
||||
session: Сессия базы данных
|
||||
user_id: ID пользователя
|
||||
count: Количество слов
|
||||
learning_lang: Язык изучения для фильтрации
|
||||
|
||||
Returns:
|
||||
Список словарей с информацией о словах и их переводах
|
||||
"""
|
||||
# Получаем слова
|
||||
words = await VocabularyService.get_random_words_for_game(
|
||||
session, user_id, count, learning_lang
|
||||
)
|
||||
|
||||
result = []
|
||||
for word in words:
|
||||
# Получаем все переводы для слова
|
||||
translations = await VocabularyService.get_word_translations(session, word.id)
|
||||
|
||||
# Собираем все варианты перевода
|
||||
all_translations = []
|
||||
|
||||
# Основной перевод из vocabulary
|
||||
if word.word_translation:
|
||||
all_translations.append(word.word_translation.lower().strip())
|
||||
|
||||
# Переводы из word_translations
|
||||
for tr in translations:
|
||||
tr_text = tr.translation.lower().strip()
|
||||
if tr_text not in all_translations:
|
||||
all_translations.append(tr_text)
|
||||
|
||||
result.append({
|
||||
'id': word.id,
|
||||
'word': word.word_original,
|
||||
'translation': word.word_translation, # Основной перевод для отображения
|
||||
'all_translations': all_translations, # Все варианты для проверки
|
||||
'transcription': word.transcription
|
||||
})
|
||||
|
||||
return result
|
||||
|
||||
Reference in New Issue
Block a user