Files
tg_bot_language/services/vocabulary_service.py

172 lines
6.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from database.models import Vocabulary, WordSource, LanguageLevel
from typing import List, Optional
import re
class VocabularyService:
"""Сервис для работы со словарным запасом"""
@staticmethod
async def add_word(
session: AsyncSession,
user_id: int,
word_original: str,
word_translation: str,
source_lang: Optional[str] = None,
translation_lang: Optional[str] = None,
transcription: Optional[str] = None,
examples: Optional[dict] = None,
category: Optional[str] = None,
difficulty_level: Optional[str] = None,
source: WordSource = WordSource.MANUAL,
notes: Optional[str] = None
) -> Vocabulary:
"""
Добавить слово в словарь пользователя
Args:
session: Сессия базы данных
user_id: ID пользователя
word_original: Оригинальное слово
word_translation: Перевод
transcription: Транскрипция
examples: Примеры использования
category: Категория слова
difficulty_level: Уровень сложности
source: Источник добавления
notes: Заметки пользователя
Returns:
Созданный объект слова
"""
# Преобразование difficulty_level в enum
difficulty_enum = None
if difficulty_level:
try:
difficulty_enum = LanguageLevel[difficulty_level]
except KeyError:
difficulty_enum = None
new_word = Vocabulary(
user_id=user_id,
word_original=word_original,
word_translation=word_translation,
source_lang=source_lang,
translation_lang=translation_lang,
transcription=transcription,
examples=examples,
category=category,
difficulty_level=difficulty_enum,
source=source,
notes=notes
)
session.add(new_word)
await session.commit()
await session.refresh(new_word)
return new_word
@staticmethod
@staticmethod
def _is_japanese(text: str) -> bool:
if not text:
return False
return re.search(r"[\u3040-\u30FF\u3400-\u4DBF\u4E00-\u9FFF]", text) is not None
@staticmethod
def _filter_by_learning_lang(words: List[Vocabulary], learning_lang: Optional[str]) -> List[Vocabulary]:
if not learning_lang:
return words
# Если в БД указан source_lang фильтруем по нему.
with_lang = [w for w in words if getattr(w, 'source_lang', None)]
if with_lang:
return [w for w in words if (w.source_lang or '').lower() == learning_lang.lower()]
# Фолбэк-эвристика для японского, если язык не сохранён
if learning_lang.lower() == 'ja':
return [w for w in words if VocabularyService._is_japanese(w.word_original)]
return [w for w in words if not VocabularyService._is_japanese(w.word_original)]
@staticmethod
async def get_user_words(session: AsyncSession, user_id: int, limit: int = 50, learning_lang: Optional[str] = None) -> List[Vocabulary]:
"""
Получить все слова пользователя
Args:
session: Сессия базы данных
user_id: ID пользователя
limit: Максимальное количество слов
Returns:
Список слов пользователя
"""
result = await session.execute(
select(Vocabulary)
.where(Vocabulary.user_id == user_id)
.order_by(Vocabulary.created_at.desc())
)
words = list(result.scalars().all())
words = VocabularyService._filter_by_learning_lang(words, learning_lang)
return words[:limit]
@staticmethod
async def get_words_count(session: AsyncSession, user_id: int, learning_lang: Optional[str] = None) -> int:
"""
Получить количество слов в словаре пользователя
Args:
session: Сессия базы данных
user_id: ID пользователя
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 len(words)
@staticmethod
async def find_word(session: AsyncSession, user_id: int, word: str) -> Optional[Vocabulary]:
"""
Найти слово в словаре пользователя
Args:
session: Сессия базы данных
user_id: ID пользователя
word: Слово для поиска
Returns:
Объект слова или None
"""
result = await session.execute(
select(Vocabulary)
.where(Vocabulary.user_id == user_id)
.where(Vocabulary.word_original.ilike(f"%{word}%"))
)
return result.scalar_one_or_none()
@staticmethod
async def get_word_by_original(session: AsyncSession, user_id: int, word: str) -> Optional[Vocabulary]:
"""
Получить слово по точному совпадению
Args:
session: Сессия базы данных
user_id: ID пользователя
word: Слово для поиска (точное совпадение)
Returns:
Объект слова или None
"""
result = await session.execute(
select(Vocabulary)
.where(Vocabulary.user_id == user_id)
.where(Vocabulary.word_original == word.lower())
)
return result.scalar_one_or_none()