initial commit (backend)
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
"""
|
||||
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,
|
||||
}
|
||||
Reference in New Issue
Block a user