import random
from datetime import datetime
from typing import List, Dict, Optional
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from database.models import Task, Vocabulary, WordTranslation
from services.ai_service import ai_service
class TaskService:
"""Сервис для работы с заданиями"""
@staticmethod
async def generate_translation_tasks(
session: AsyncSession,
user_id: int,
count: int = 5
) -> List[Dict]:
"""
Генерация заданий на перевод слов
Args:
session: Сессия базы данных
user_id: ID пользователя
count: Количество заданий
Returns:
Список заданий
"""
# Получаем слова пользователя
result = await session.execute(
select(Vocabulary)
.where(Vocabulary.user_id == user_id)
.order_by(Vocabulary.last_reviewed.asc().nullsfirst())
.limit(count * 2) # Берем больше, чтобы было из чего выбрать
)
words = list(result.scalars().all())
if not words:
return []
# Выбираем случайные слова
selected_words = random.sample(words, min(count, len(words)))
tasks = []
for word in selected_words:
# Случайно выбираем направление перевода
direction = random.choice(['en_to_ru', 'ru_to_en'])
if direction == 'en_to_ru':
task = {
'type': 'translate_to_ru',
'word_id': word.id,
'question': f"Переведи слово: {word.word_original}",
'word': word.word_original,
'correct_answer': word.word_translation,
'transcription': word.transcription
}
else:
task = {
'type': 'translate_to_en',
'word_id': word.id,
'question': f"Переведи слово: {word.word_translation}",
'word': word.word_translation,
'correct_answer': word.word_original,
'transcription': word.transcription
}
tasks.append(task)
return tasks
@staticmethod
async def generate_mixed_tasks(
session: AsyncSession,
user_id: int,
count: int = 5,
learning_lang: str = 'en',
translation_lang: str = 'ru'
) -> List[Dict]:
"""
Генерация заданий разных типов (переводы + заполнение пропусков)
Args:
session: Сессия базы данных
user_id: ID пользователя
count: Количество заданий
Returns:
Список заданий разных типов
"""
# Получаем слова пользователя
result = await session.execute(
select(Vocabulary)
.where(Vocabulary.user_id == user_id)
.order_by(Vocabulary.last_reviewed.asc().nullsfirst())
.limit(count * 2)
)
words = list(result.scalars().all())
if not words:
return []
# Выбираем случайные слова
selected_words = random.sample(words, min(count, len(words)))
tasks = []
for word in selected_words:
# Получаем переводы из таблицы WordTranslation
translations_result = await session.execute(
select(WordTranslation)
.where(WordTranslation.vocabulary_id == word.id)
.order_by(WordTranslation.is_primary.desc())
)
translations = list(translations_result.scalars().all())
# Случайно выбираем тип задания
# Если есть переводы с контекстом, добавляем тип 'context_translate'
task_types = ['translate', 'fill_in']
if translations and any(tr.context for tr in translations):
task_types.append('context_translate')
task_type = random.choice(task_types)
if task_type == 'context_translate' and translations:
# Задание на перевод по контексту
# Выбираем случайный перевод с контекстом
translations_with_context = [tr for tr in translations if tr.context]
if translations_with_context:
selected_tr = random.choice(translations_with_context)
# Локализация фразы
if translation_lang == 'en':
prompt = "Translate the highlighted word in context:"
elif translation_lang == 'ja':
prompt = "文脈に合った翻訳を入力してください:"
else:
prompt = "Переведи выделенное слово в контексте:"
task = {
'type': 'context_translate',
'word_id': word.id,
'translation_id': selected_tr.id,
'question': (
f"{prompt}\n\n"
f"«{selected_tr.context}»\n\n"
f"{word.word_original} = ?"
),
'word': word.word_original,
'correct_answer': selected_tr.translation,
'transcription': word.transcription,
'context': selected_tr.context,
'context_translation': selected_tr.context_translation
}
tasks.append(task)
continue
if task_type == 'translate':
# Задание на перевод между языком обучения и языком перевода
direction = random.choice(['learn_to_tr', 'tr_to_learn'])
# Локализация фразы "Переведи слово"
if translation_lang == 'en':
prompt = "Translate the word:"
elif translation_lang == 'ja':
prompt = "単語を訳してください:"
else:
prompt = "Переведи слово:"
# Определяем правильный ответ - берём из таблицы переводов если есть
correct_translation = word.word_translation
if translations:
# Берём основной перевод или первый
primary = next((tr for tr in translations if tr.is_primary), translations[0] if translations else None)
if primary:
correct_translation = primary.translation
if direction == 'learn_to_tr':
task = {
'type': f'translate_to_{translation_lang}',
'word_id': word.id,
'question': f"{prompt} {word.word_original}",
'word': word.word_original,
'correct_answer': correct_translation,
'transcription': word.transcription,
# Все допустимые ответы для проверки
'all_translations': [tr.translation for tr in translations] if translations else [correct_translation]
}
else:
task = {
'type': f'translate_to_{learning_lang}',
'word_id': word.id,
'question': f"{prompt} {correct_translation}",
'word': correct_translation,
'correct_answer': word.word_original,
'transcription': word.transcription
}
else:
# Задание на заполнение пропуска
# Генерируем предложение с пропуском через AI
sentence_data = await ai_service.generate_fill_in_sentence(
word.word_original,
learning_lang=learning_lang,
translation_lang=translation_lang
)
# Локализация заголовка
if translation_lang == 'en':
fill_title = "Fill in the blank in the sentence:"
elif translation_lang == 'ja':
fill_title = "文の空欄を埋めてください:"
else:
fill_title = "Заполни пропуск в предложении:"
task = {
'type': 'fill_in',
'word_id': word.id,
'question': (
f"{fill_title}\n\n"
f"{sentence_data['sentence']}\n\n"
f"{sentence_data.get('translation', '')}"
),
'word': word.word_original,
'correct_answer': sentence_data['answer'],
'sentence': sentence_data['sentence']
}
tasks.append(task)
return tasks
@staticmethod
async def save_task_result(
session: AsyncSession,
user_id: int,
task_type: str,
content: Dict,
user_answer: str,
correct_answer: str,
is_correct: bool,
ai_feedback: Optional[str] = None
) -> Task:
"""
Сохранение результата выполнения задания
Args:
session: Сессия базы данных
user_id: ID пользователя
task_type: Тип задания
content: Содержимое задания
user_answer: Ответ пользователя
correct_answer: Правильный ответ
is_correct: Правильность ответа
ai_feedback: Обратная связь от AI
Returns:
Сохраненное задание
"""
task = Task(
user_id=user_id,
task_type=task_type,
content=content,
user_answer=user_answer,
correct_answer=correct_answer,
is_correct=is_correct,
ai_feedback=ai_feedback,
completed_at=datetime.utcnow()
)
session.add(task)
await session.commit()
await session.refresh(task)
return task
@staticmethod
async def update_word_statistics(
session: AsyncSession,
word_id: int,
is_correct: bool
):
"""
Обновление статистики слова
Args:
session: Сессия базы данных
word_id: ID слова
is_correct: Правильность ответа
"""
result = await session.execute(
select(Vocabulary).where(Vocabulary.id == word_id)
)
word = result.scalar_one_or_none()
if word:
word.times_reviewed += 1
if is_correct:
word.correct_answers += 1
word.last_reviewed = datetime.utcnow()
await session.commit()
@staticmethod
async def get_user_stats(session: AsyncSession, user_id: int) -> Dict:
"""
Получение статистики пользователя
Args:
session: Сессия базы данных
user_id: ID пользователя
Returns:
Статистика пользователя
"""
# Количество слов
words_result = await session.execute(
select(Vocabulary).where(Vocabulary.user_id == user_id)
)
words = list(words_result.scalars().all())
total_words = len(words)
# Количество выполненных заданий
tasks_result = await session.execute(
select(Task).where(Task.user_id == user_id)
)
tasks = list(tasks_result.scalars().all())
total_tasks = len(tasks)
# Правильные ответы
correct_tasks = len([t for t in tasks if t.is_correct])
accuracy = int((correct_tasks / total_tasks * 100)) if total_tasks > 0 else 0
# Слова с повторениями
reviewed_words = len([w for w in words if w.times_reviewed > 0])
return {
'total_words': total_words,
'reviewed_words': reviewed_words,
'total_tasks': total_tasks,
'correct_tasks': correct_tasks,
'accuracy': accuracy
}
@staticmethod
async def get_correctly_answered_words(
session: AsyncSession,
user_id: int
) -> List[str]:
"""
Получить список слов, на которые пользователь правильно ответил в заданиях
Args:
session: Сессия базы данных
user_id: ID пользователя
Returns:
Список слов (строки) с правильными ответами
"""
result = await session.execute(
select(Task)
.where(Task.user_id == user_id)
.where(Task.is_correct == True)
)
tasks = list(result.scalars().all())
words = []
for task in tasks:
if task.content and isinstance(task.content, dict):
word = task.content.get('word')
if word:
words.append(word.lower())
return list(set(words))