""" Typing Router — раздел "Печать". Пользователь печатает польские предложения, система проверяет. Без AI — просто сравнение с правильным ответом из БД. """ import random from fastapi import APIRouter, Request, HTTPException from auth import require_auth from database import get_pool router = APIRouter(prefix="/typing", tags=["typing"]) def normalize_text(text: str) -> str: """Нормализует текст для сравнения: нижний регистр, убирает лишние пробелы.""" return ' '.join(text.strip().lower().split()) @router.get("/next") async def get_next_typing_sentence(request: Request): """Выдаёт случайное непройденное предложение для выбранного уровня.""" telegram_id, _ = await require_auth(request) pool = await get_pool() async with pool.acquire() as conn: user = await conn.fetchrow(""" SELECT app_language, current_level FROM users WHERE telegram_id = $1 """, telegram_id) if not user: raise HTTPException(401, "User not registered") lang = user["app_language"] or "pl" current_level = user["current_level"] or "A1" # Находим случайное непройденное предложение row = await conn.fetchrow(""" SELECT s.id, s.sentence_pl, s.translation_ru, s.translation_uk, s.translation_en, s.level FROM sentences s WHERE s.level = $1 AND s.id NOT IN ( SELECT sentence_id FROM typing_progress WHERE user_id = $2 ) ORDER BY RANDOM() LIMIT 1 """, current_level, telegram_id) if not row: return {"completed": True, "message": "Все предложения пройдены!"} translation_field = f"translation_{lang}" translation = row.get(translation_field) or row.get("translation_en") or row["sentence_pl"] return { "completed": False, "sentence_id": row["id"], "translation": translation, "level": row["level"], } @router.post("/check") async def check_typing_sentence(request: Request): """Проверяет напечатанное предложение.""" telegram_id, _ = await require_auth(request) body = await request.json() sentence_id = body.get("sentence_id") user_text = body.get("text", "") if not sentence_id or not user_text: raise HTTPException(400, "sentence_id and text required") pool = await get_pool() async with pool.acquire() as conn: row = await conn.fetchrow(""" SELECT sentence_pl FROM sentences WHERE id = $1 """, sentence_id) if not row: raise HTTPException(404, "Sentence not found") correct_text = normalize_text(row["sentence_pl"]) user_normalized = normalize_text(user_text) is_correct = correct_text == user_normalized if is_correct: # Сохраняем прогресс await conn.execute(""" INSERT INTO typing_progress (user_id, sentence_id, completed_at) VALUES ($1, $2, NOW()) ON CONFLICT (user_id, sentence_id) DO NOTHING """, telegram_id, sentence_id) return { "correct": is_correct, "correct_answer": row["sentence_pl"], } @router.get("/hint/{sentence_id}") async def get_typing_hint(request: Request, sentence_id: int): """Возвращает правильный ответ для подсказки.""" await require_auth(request) pool = await get_pool() async with pool.acquire() as conn: row = await conn.fetchrow(""" SELECT sentence_pl FROM sentences WHERE id = $1 """, sentence_id) if not row: raise HTTPException(404, "Sentence not found") return {"correct_answer": row["sentence_pl"]} @router.get("/progress") async def get_typing_progress(request: Request): """Статистика по печати.""" telegram_id, _ = await require_auth(request) pool = await get_pool() async with pool.acquire() as conn: user = await conn.fetchrow(""" SELECT current_level FROM users WHERE telegram_id = $1 """, telegram_id) current_level = user["current_level"] if user else "A1" total = await conn.fetchval(""" SELECT COUNT(*) FROM sentences WHERE level = $1 """, current_level) or 0 completed = await conn.fetchval(""" SELECT COUNT(*) FROM typing_progress tp JOIN sentences s ON tp.sentence_id = s.id WHERE tp.user_id = $1 AND s.level = $2 """, telegram_id, current_level) or 0 return { "total": total, "completed": completed, "remaining": total - completed, }