feat: multiple translations with context, improved task examples
- Add WordTranslation model for storing multiple translations per word - AI generates translations with example sentences and their translations - Show example usage after answering tasks (learning + interface language) - Save translations to word_translations table when adding words from tasks - Improve word exclusion in new_words mode (stronger prompt + client filtering) - Add migration for word_translations table 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from database.models import Vocabulary, WordSource, LanguageLevel
|
||||
from typing import List, Optional
|
||||
from database.models import Vocabulary, WordSource, LanguageLevel, WordTranslation
|
||||
from typing import List, Optional, Dict
|
||||
import re
|
||||
|
||||
|
||||
@@ -176,3 +176,183 @@ class VocabularyService:
|
||||
.where(Vocabulary.word_original == word.lower())
|
||||
)
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
@staticmethod
|
||||
async def get_all_user_word_strings(
|
||||
session: AsyncSession,
|
||||
user_id: int,
|
||||
learning_lang: Optional[str] = None
|
||||
) -> List[str]:
|
||||
"""
|
||||
Получить список всех слов пользователя (только строки)
|
||||
|
||||
Args:
|
||||
session: Сессия базы данных
|
||||
user_id: ID пользователя
|
||||
learning_lang: Язык изучения для фильтрации
|
||||
|
||||
Returns:
|
||||
Список строк — оригинальных слов
|
||||
"""
|
||||
result = await session.execute(
|
||||
select(Vocabulary)
|
||||
.where(Vocabulary.user_id == user_id)
|
||||
)
|
||||
words = list(result.scalars().all())
|
||||
words = VocabularyService._filter_by_learning_lang(words, learning_lang)
|
||||
return [w.word_original.lower() for w in words]
|
||||
|
||||
# === Методы для работы с переводами ===
|
||||
|
||||
@staticmethod
|
||||
async def add_translation(
|
||||
session: AsyncSession,
|
||||
vocabulary_id: int,
|
||||
translation: str,
|
||||
context: Optional[str] = None,
|
||||
context_translation: Optional[str] = None,
|
||||
is_primary: bool = False
|
||||
) -> WordTranslation:
|
||||
"""
|
||||
Добавить перевод к слову
|
||||
|
||||
Args:
|
||||
session: Сессия базы данных
|
||||
vocabulary_id: ID слова в словаре
|
||||
translation: Перевод
|
||||
context: Пример предложения на языке изучения
|
||||
context_translation: Перевод примера
|
||||
is_primary: Является ли основным переводом
|
||||
|
||||
Returns:
|
||||
Созданный объект перевода
|
||||
"""
|
||||
# Если это основной перевод, снимаем флаг с других
|
||||
if is_primary:
|
||||
result = await session.execute(
|
||||
select(WordTranslation)
|
||||
.where(WordTranslation.vocabulary_id == vocabulary_id)
|
||||
.where(WordTranslation.is_primary == True)
|
||||
)
|
||||
for existing in result.scalars().all():
|
||||
existing.is_primary = False
|
||||
|
||||
new_translation = WordTranslation(
|
||||
vocabulary_id=vocabulary_id,
|
||||
translation=translation,
|
||||
context=context,
|
||||
context_translation=context_translation,
|
||||
is_primary=is_primary
|
||||
)
|
||||
|
||||
session.add(new_translation)
|
||||
await session.commit()
|
||||
await session.refresh(new_translation)
|
||||
|
||||
return new_translation
|
||||
|
||||
@staticmethod
|
||||
async def add_translations_bulk(
|
||||
session: AsyncSession,
|
||||
vocabulary_id: int,
|
||||
translations: List[Dict]
|
||||
) -> List[WordTranslation]:
|
||||
"""
|
||||
Добавить несколько переводов к слову
|
||||
|
||||
Args:
|
||||
session: Сессия базы данных
|
||||
vocabulary_id: ID слова
|
||||
translations: Список словарей с переводами
|
||||
[{"translation": "...", "context": "...", "context_translation": "...", "is_primary": bool}]
|
||||
|
||||
Returns:
|
||||
Список созданных переводов
|
||||
"""
|
||||
created = []
|
||||
for i, tr_data in enumerate(translations):
|
||||
new_translation = WordTranslation(
|
||||
vocabulary_id=vocabulary_id,
|
||||
translation=tr_data.get('translation', ''),
|
||||
context=tr_data.get('context'),
|
||||
context_translation=tr_data.get('context_translation'),
|
||||
is_primary=tr_data.get('is_primary', i == 0) # Первый по умолчанию основной
|
||||
)
|
||||
session.add(new_translation)
|
||||
created.append(new_translation)
|
||||
|
||||
await session.commit()
|
||||
for tr in created:
|
||||
await session.refresh(tr)
|
||||
|
||||
return created
|
||||
|
||||
@staticmethod
|
||||
async def get_word_translations(
|
||||
session: AsyncSession,
|
||||
vocabulary_id: int
|
||||
) -> List[WordTranslation]:
|
||||
"""
|
||||
Получить все переводы слова
|
||||
|
||||
Args:
|
||||
session: Сессия базы данных
|
||||
vocabulary_id: ID слова
|
||||
|
||||
Returns:
|
||||
Список переводов
|
||||
"""
|
||||
result = await session.execute(
|
||||
select(WordTranslation)
|
||||
.where(WordTranslation.vocabulary_id == vocabulary_id)
|
||||
.order_by(WordTranslation.is_primary.desc(), WordTranslation.created_at)
|
||||
)
|
||||
return list(result.scalars().all())
|
||||
|
||||
@staticmethod
|
||||
async def get_primary_translation(
|
||||
session: AsyncSession,
|
||||
vocabulary_id: int
|
||||
) -> Optional[WordTranslation]:
|
||||
"""
|
||||
Получить основной перевод слова
|
||||
|
||||
Args:
|
||||
session: Сессия базы данных
|
||||
vocabulary_id: ID слова
|
||||
|
||||
Returns:
|
||||
Основной перевод или None
|
||||
"""
|
||||
result = await session.execute(
|
||||
select(WordTranslation)
|
||||
.where(WordTranslation.vocabulary_id == vocabulary_id)
|
||||
.where(WordTranslation.is_primary == True)
|
||||
)
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
@staticmethod
|
||||
async def delete_translation(
|
||||
session: AsyncSession,
|
||||
translation_id: int
|
||||
) -> bool:
|
||||
"""
|
||||
Удалить перевод
|
||||
|
||||
Args:
|
||||
session: Сессия базы данных
|
||||
translation_id: ID перевода
|
||||
|
||||
Returns:
|
||||
True если удалено, False если не найдено
|
||||
"""
|
||||
result = await session.execute(
|
||||
select(WordTranslation).where(WordTranslation.id == translation_id)
|
||||
)
|
||||
translation = result.scalar_one_or_none()
|
||||
|
||||
if translation:
|
||||
await session.delete(translation)
|
||||
await session.commit()
|
||||
return True
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user