initial commit (backend)

This commit is contained in:
2026-09-20 11:45:58 +00:00
commit 033adf023e
65 changed files with 28292 additions and 0 deletions
+19
View File
@@ -0,0 +1,19 @@
BOT_TOKEN=8886420359:AAEZgOsEBEk3pthIEJboUIb0MJZk0mGFX-A
DB_PASSWORD=filwords123
BOT_SECRET=filwords_secret_2024_random_string_here
ADMIN_IDS=6694989403
FRONTEND_ORIGIN=https://filwordspl.com
DB_USER=filwords
DB_NAME=filwords
DB_HOST=localhost
DB_PORT=5432
TOGETHER_API_KEY=tgp_v1_7aSO98nDDhBtZS2sq1_Q1Q61AD7V1TrKKAiNbJlsusc
SENTRY_DSN=https://a285ea156048a1adc7f0d00b3d49e90c@o4512063249121280.ingest.de.sentry.io/4512063274090576
ENV=production
# Версия приложения (используется в Sentry release)
APP_VERSION=3.3.8щ
SENTRY_AUTH_TOKEN=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
SENTRY_ORG_SLUG=drobysh
SENTRY_PROJECT_SLUG=filwords-backend
SENTRY_API_BASE=https://de.sentry.io/api/0
+9
View File
@@ -0,0 +1,9 @@
.env
venv/
__pycache__/
*.pyc
tts_cache/
archive/
uploads/
*.log
.DS_Store
View File
+258
View File
@@ -0,0 +1,258 @@
"""
Безопасное добавление уровней без удаления старых.
Использует только words.csv.
"""
import asyncio
import asyncpg
import json
import random
import csv
from config import DB_CONFIG
DIRECTIONS = [(-1,0),(0,-1),(0,1),(1,0)]
ALPHABET = "AĄBCĆDEĘFGHIJKLŁMNŃOÓPRSŚTUWYZŹŻ"
def load_words_from_csv(filenames):
words_by_theme = {}
translations = {}
for filename in filenames:
with open(filename, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
theme = row['theme']
word = row['polish'].lower()
if theme not in words_by_theme:
words_by_theme[theme] = []
if word not in words_by_theme[theme]:
words_by_theme[theme].append(word)
translations[word] = {
'en': row['english'],
'ru': row['russian'],
'uk': row['ukrainian']
}
return words_by_theme, translations
def get_words_for_grid(words_by_theme, theme, num_words, grid_size):
max_len = grid_size
candidates = [w for w in words_by_theme[theme] if len(w) <= max_len]
if len(candidates) < num_words:
candidates = words_by_theme[theme]
random.shuffle(candidates)
return candidates[:num_words]
def try_place_word(word, grid_size, occupied, max_attempts=500):
word = word.upper()
for _ in range(max_attempts):
start = (random.randrange(grid_size), random.randrange(grid_size))
if start in occupied:
continue
path = [start]
visited = {start}
ok = True
for _ in range(len(word) - 1):
r, c = path[-1]
candidates = []
for dr, dc in DIRECTIONS:
nr, nc = r + dr, c + dc
if 0 <= nr < grid_size and 0 <= nc < grid_size:
if (nr, nc) not in occupied and (nr, nc) not in visited:
candidates.append((nr, nc))
if not candidates:
ok = False
break
nxt = random.choice(candidates)
path.append(nxt)
visited.add(nxt)
if ok and len(path) == len(word):
return path
return None
def fill_empty_cells(grid, grid_size, occupied, words_by_theme, all_words):
empty_cells = set()
for r in range(grid_size):
for c in range(grid_size):
if (r, c) not in occupied:
empty_cells.add((r, c))
if not empty_cells:
return [], {}
filler_words = []
for theme in words_by_theme:
for w in words_by_theme[theme]:
if 2 <= len(w) <= 4 and w not in all_words:
filler_words.append(w)
filler_words = sorted(list(set(filler_words)), key=len, reverse=True)
random.shuffle(filler_words)
filled_paths = {}
filled_words = []
remaining_empty = empty_cells.copy()
for word in filler_words:
if len(word) > len(remaining_empty):
continue
for _ in range(500):
if not remaining_empty:
break
start = random.choice(list(remaining_empty))
path = [start]
visited = {start}
ok = True
for _ in range(len(word) - 1):
r, c = path[-1]
candidates = []
for dr, dc in DIRECTIONS:
nr, nc = r + dr, c + dc
if 0 <= nr < grid_size and 0 <= nc < grid_size:
if (nr, nc) in remaining_empty and (nr, nc) not in visited:
candidates.append((nr, nc))
if not candidates:
ok = False
break
nxt = random.choice(candidates)
path.append(nxt)
visited.add(nxt)
if ok and len(path) == len(word):
for (r, c), letter in zip(path, word.upper()):
grid[r][c] = letter
if (r, c) in remaining_empty:
remaining_empty.remove((r, c))
filled_paths[word] = [list(p) for p in path]
filled_words.append(word)
break
for r, c in remaining_empty:
grid[r][c] = random.choice(ALPHABET)
return filled_words, filled_paths
def generate_grid(words, grid_size, words_by_theme, max_restarts=200):
words_sorted = sorted(words, key=len, reverse=True)
for _ in range(max_restarts):
grid = [['' for _ in range(grid_size)] for _ in range(grid_size)]
occupied = set()
word_paths = {}
success = True
for word in words_sorted:
path = try_place_word(word, grid_size, occupied)
if path is None:
success = False
break
for (r, c), letter in zip(path, word.upper()):
grid[r][c] = letter
occupied.update(path)
word_paths[word] = [list(p) for p in path]
if not success:
continue
all_words = set(words_sorted)
filled_words, filled_paths = fill_empty_cells(
grid, grid_size, occupied, words_by_theme, all_words
)
word_paths.update(filled_paths)
all_placed_words = words_sorted + filled_words
return grid, word_paths, all_placed_words
return None, None, None
async def save_level(conn, level_num, grid_size, theme, grid, word_paths, words, translations):
level = await conn.fetchrow(
"""INSERT INTO levels (level_number, grid_size, theme, grid_letters, word_paths)
VALUES ($1, $2, $3, $4, $5) RETURNING id""",
level_num, grid_size, theme, json.dumps(grid), json.dumps(word_paths)
)
level_id = level['id']
for word in words:
trans = translations.get(word, {})
await conn.execute(
"""INSERT INTO level_words (level_id, word_pl, translation_ru, translation_uk, translation_en)
VALUES ($1, $2, $3, $4, $5)""",
level_id, word,
trans.get('ru', ''),
trans.get('uk', ''),
trans.get('en', '')
)
async def main():
# ============ НАСТРОЙКИ ============
words_by_theme, translations = load_words_from_csv([
'words.csv'
])
NEW_LEVEL_CONFIG = [
(25, 10, 9), # 25 уровней: 10x10, 9 слов
(25, 10, 10), # 25 уровней: 10x10, 10 слов
(25, 10, 11), # 25 уровней: 10x10, 11 слов
(25, 10, 12), # 25 уровней: 10x10, 12 слов
]
# ==================================
conn = await asyncpg.connect(**DB_CONFIG)
current_max = await conn.fetchval("SELECT COALESCE(MAX(level_number), 0) FROM levels")
print(f"Сейчас уровней в базе: {current_max}")
print(f"Добавляем уровни {current_max + 1}{current_max + 100}")
themes = list(words_by_theme.keys())
random.shuffle(themes)
level_num = current_max
theme_idx = 0
total_generated = 0
for count, grid_size, num_words in NEW_LEVEL_CONFIG:
for i in range(count):
level_num += 1
for attempt in range(15):
theme = themes[theme_idx % len(themes)]
theme_idx += 1
words = get_words_for_grid(words_by_theme, theme, num_words, grid_size)
if len(words) < num_words:
continue
grid, word_paths, all_words = generate_grid(
words, grid_size, words_by_theme
)
if grid is not None:
await save_level(
conn, level_num, grid_size, theme,
grid, word_paths, all_words, translations
)
total_cells = grid_size * grid_size
filled_cells = sum(len(w) for w in all_words)
print(f"✅ Level {level_num}: {grid_size}x{grid_size}, "
f"theme={theme}, words={len(all_words)} ({num_words} main), "
f"fill={filled_cells}/{total_cells}")
total_generated += 1
break
else:
print(f"❌ FAILED: Level {level_num} after 15 attempts")
level_num -= 1
await conn.close()
print(f"\n🎉 Готово! Добавлено {total_generated} новых уровней. Теперь всего уровней: {level_num}")
if __name__ == "__main__":
asyncio.run(main())
+75
View File
@@ -0,0 +1,75 @@
"""
Загрузка предложений по уровням (A1, A2, B1).
Каждый уровень загружается из отдельного CSV файла.
Нумерация предложений начинается с 1 для каждого уровня.
"""
import asyncio
import asyncpg
import csv
from config import DB_CONFIG
def load_sentences_from_csv(filename):
"""Загружает предложения из CSV файла (формат: polish,english,russian,ukrainian)."""
sentences = []
with open(filename, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
sentences.append({
"sentence_pl": row["polish"].strip(),
"translation_ru": row.get("russian", ""),
"translation_uk": row.get("ukrainian", ""),
"translation_en": row.get("english", ""),
})
return sentences
async def main():
conn = await asyncpg.connect(**DB_CONFIG)
level_files = [
("A1", "sentences_a1.csv"),
("A2", "sentences_a2.csv"),
("B1", "sentences_b1.csv"),
]
total_added = 0
for level, filename in level_files:
print(f"\n{'='*50}")
print(f"Загружаю предложения уровня {level} из {filename}")
print(f"{'='*50}")
sentences = load_sentences_from_csv(filename)
# Удаляем старые предложения этого уровня
await conn.execute("DELETE FROM sentences WHERE level = $1", level)
# Нумерация начинается с 1 для каждого уровня
max_num = 0
added = 0
for s in sentences:
max_num += 1
words_count = len(s["sentence_pl"].split())
difficulty = 1 if words_count <= 4 else 2 if words_count <= 6 else 3
await conn.execute(
"""INSERT INTO sentences (sentence_number, sentence_pl, translation_ru, translation_uk, translation_en, difficulty, level)
VALUES ($1, $2, $3, $4, $5, $6, $7)""",
max_num, s["sentence_pl"],
s["translation_ru"], s["translation_uk"], s["translation_en"],
difficulty, level
)
added += 1
print(f"{level} #{max_num}: {s['sentence_pl'][:60]}...")
total_added += added
print(f"Добавлено {added} предложений уровня {level}")
await conn.close()
print(f"\n🎉 Готово! Всего добавлено {total_added} предложений")
if __name__ == "__main__":
asyncio.run(main())
+221
View File
@@ -0,0 +1,221 @@
"""
Архивация старых данных:
1. Ответы старше 30 дней → user_learning_answers_archive (агрегаты)
2. Данные архива старше 1 года → JSON файлы на диске
3. Удаление из основных таблиц
Запуск: python3 archive_old_data.py
Или через cron: 0 3 * * * cd /root/filwordspl/backend && source venv/bin/activate && python3 archive_old_data.py
"""
import asyncio
import asyncpg
import json
import os
import logging
from datetime import datetime, timedelta, timezone
from decimal import Decimal
from pathlib import Path
from config import DB_CONFIG
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# Настройки
HOT_DAYS = 30 # Сколько дней хранить в основной таблице
ARCHIVE_DIR = Path("/root/filwordspl/backend/archive") # Директория для JSON файлов
def serialize_value(value):
"""Преобразует Decimal и datetime в JSON-совместимые типы."""
if value is None:
return None
if hasattr(value, 'isoformat'):
return value.isoformat()
if isinstance(value, Decimal):
return float(value)
if isinstance(value, (int, float, bool, str)):
return value
return str(value)
async def archive_learning_answers(conn):
"""Переносит ответы старше 30 дней в архивную таблицу."""
# Переносим в архив
moved = await conn.execute("""
INSERT INTO user_learning_answers_archive (user_id, skill_tag, correct, wrong_because, created_at)
SELECT user_id, skill_tag, correct, wrong_because, created_at::date
FROM user_learning_answers
WHERE created_at < NOW() - INTERVAL '30 days'
""")
# Удаляем из основной таблицы
deleted = await conn.execute("""
DELETE FROM user_learning_answers
WHERE created_at < NOW() - INTERVAL '30 days'
""")
logger.info(f"Архивация ответов: перенесено={moved}, удалено={deleted}")
return moved, deleted
async def export_archive_to_json(conn):
"""Экспортирует данные архива старше 1 года в JSON файлы."""
# Получаем данные старше 1 года
old_records = await conn.fetch("""
SELECT * FROM user_learning_answers_archive
WHERE created_at < NOW() - INTERVAL '1 year'
""")
if not old_records:
logger.info("Нет данных старше 1 года для экспорта")
return 0
# Группируем по году и пользователю
grouped = {}
for record in old_records:
year = record["created_at"].year if record["created_at"] else datetime.now().year
user_id = record["user_id"]
if year not in grouped:
grouped[year] = {}
if user_id not in grouped[year]:
grouped[year][user_id] = []
grouped[year][user_id].append({
"id": record["id"],
"user_id": record["user_id"],
"skill_tag": record["skill_tag"],
"correct": record["correct"],
"wrong_because": record["wrong_because"],
"created_at": serialize_value(record["created_at"]),
"archived_at": serialize_value(record["archived_at"]),
})
# Сохраняем в JSON файлы
total_saved = 0
for year, users in grouped.items():
year_dir = ARCHIVE_DIR / str(year)
year_dir.mkdir(parents=True, exist_ok=True)
for user_id, records in users.items():
file_path = year_dir / f"user_{user_id}_answers.json"
# Если файл существует — дополняем
existing_data = []
if file_path.exists():
try:
with open(file_path, 'r', encoding='utf-8') as f:
existing_data = json.load(f)
except:
existing_data = []
all_data = existing_data + records
with open(file_path, 'w', encoding='utf-8') as f:
json.dump(all_data, f, ensure_ascii=False, indent=2)
total_saved += len(records)
# Удаляем экспортированные записи из архива
if old_records:
ids = [r["id"] for r in old_records]
await conn.execute("""
DELETE FROM user_learning_answers_archive WHERE id = ANY($1)
""", ids)
logger.info(f"Экспортировано в JSON: {total_saved} записей")
return total_saved
async def archive_chat_history(conn):
"""Архивирует старую историю чата (старше 6 месяцев)."""
# Экспортируем старую историю в JSON
old_messages = await conn.fetch("""
SELECT * FROM chat_history
WHERE created_at < NOW() - INTERVAL '6 months'
""")
if old_messages:
# Группируем по году и пользователю
grouped = {}
for msg in old_messages:
year = msg["created_at"].year if msg["created_at"] else datetime.now().year
user_id = msg["user_id"]
if year not in grouped:
grouped[year] = {}
if user_id not in grouped[year]:
grouped[year][user_id] = []
grouped[year][user_id].append({
"id": msg["id"],
"user_id": msg["user_id"],
"message": msg["message"],
"is_user": msg["is_user"],
"created_at": serialize_value(msg["created_at"]),
})
# Сохраняем в JSON
total_saved = 0
for year, users in grouped.items():
year_dir = ARCHIVE_DIR / str(year)
year_dir.mkdir(parents=True, exist_ok=True)
for user_id, messages in users.items():
file_path = year_dir / f"user_{user_id}_chat.json"
existing_data = []
if file_path.exists():
try:
with open(file_path, 'r', encoding='utf-8') as f:
existing_data = json.load(f)
except:
existing_data = []
all_data = existing_data + messages
with open(file_path, 'w', encoding='utf-8') as f:
json.dump(all_data, f, ensure_ascii=False, indent=2)
total_saved += len(messages)
# Удаляем старые сообщения
await conn.execute("""
DELETE FROM chat_history
WHERE created_at < NOW() - INTERVAL '6 months'
""")
logger.info(f"Архивация чата: {total_saved} сообщений в JSON")
return total_saved
logger.info("Нет старой истории чата для архивации")
return 0
async def main():
logger.info("=== Начало архивации ===")
conn = await asyncpg.connect(**DB_CONFIG)
try:
# 1. Архивация ответов
moved, deleted = await archive_learning_answers(conn)
# 2. Экспорт архива в JSON
exported = await export_archive_to_json(conn)
# 3. Архивация чата
chat_archived = await archive_chat_history(conn)
logger.info(f"=== Архивация завершена: ответы={moved}, JSON={exported}, чат={chat_archived} ===")
except Exception as e:
logger.error(f"Ошибка архивации: {str(e)}")
finally:
await conn.close()
if __name__ == "__main__":
asyncio.run(main())
+108
View File
@@ -0,0 +1,108 @@
import time
import hmac
import hashlib
import json
from urllib.parse import unquote
from fastapi import Request, HTTPException
from config import BOT_TOKEN, MAX_INITDATA_AGE
async def verify_telegram_initdata(init_data: str):
"""Проверяет подпись initData от Telegram с проверкой срока действия"""
try:
# Парсим initData
data = {}
for chunk in init_data.split('&'):
if '=' in chunk:
key, value = chunk.split('=', 1)
data[key] = unquote(value)
# Извлекаем hash
received_hash = data.pop('hash', '')
if not received_hash:
raise HTTPException(401, "Hash not found in initData")
# Проверяем auth_date (не старше MAX_INITDATA_AGE из config)
auth_date_str = data.get('auth_date', '0')
try:
auth_date = int(auth_date_str)
current_time = int(time.time())
if current_time - auth_date > MAX_INITDATA_AGE:
raise HTTPException(401, "initData expired")
if auth_date > current_time + 60:
raise HTTPException(401, "initData from future")
except ValueError:
raise HTTPException(401, "Invalid auth_date")
# Сортируем ключи и создаем строку для проверки
check_arr = []
for key in sorted(data.keys()):
check_arr.append(f"{key}={data[key]}")
check_string = '\n'.join(check_arr)
# Создаем секретный ключ
secret_key = hmac.new(
'WebAppData'.encode(),
BOT_TOKEN.encode(),
hashlib.sha256
).digest()
# Вычисляем hash
calculated_hash = hmac.new(
secret_key,
check_string.encode(),
hashlib.sha256
).hexdigest()
if calculated_hash != received_hash:
raise HTTPException(401, "Invalid initData signature")
# Извлекаем данные пользователя
user_data_str = data.get('user', '{}')
try:
user_data = json.loads(user_data_str)
except json.JSONDecodeError:
raise HTTPException(401, "Invalid user data JSON")
user_id = user_data.get('id')
username = user_data.get('username', '')
first_name = user_data.get('first_name', '')
if not user_id:
raise HTTPException(401, "User ID not found in initData")
return user_id, username
except HTTPException:
raise
except Exception as e:
raise HTTPException(401, f"Auth error: {str(e)}")
async def require_auth(request: Request):
"""Извлекает и проверяет initData из запроса"""
# Пробуем из query-параметров
init_data = request.query_params.get('initData', '')
if not init_data:
# Пробуем из заголовков
init_data = request.headers.get('X-Telegram-InitData', '')
if not init_data:
# Пробуем из тела для POST запросов
# (некоторые клиенты могут отправлять initData в теле)
pass
if not init_data:
raise HTTPException(401, "initData required. Pass as query parameter 'initData' or header 'X-Telegram-InitData'")
# Декодируем URL-encoded initData если нужно
if '%' in init_data:
init_data = unquote(init_data)
user_id, username = await verify_telegram_initdata(init_data)
return user_id, username
+63
View File
@@ -0,0 +1,63 @@
import os
from dotenv import load_dotenv
load_dotenv()
# Проверка критических переменных
for var in ["BOT_TOKEN", "DB_PASSWORD", "BOT_SECRET"]:
if var not in os.environ:
raise RuntimeError(f"Missing required environment variable: {var}")
BOT_TOKEN = os.environ["BOT_TOKEN"]
BOT_SECRET = os.environ["BOT_SECRET"]
# Together AI для AI-помощника
TOGETHER_API_KEY = os.getenv("TOGETHER_API_KEY", "")
DB_CONFIG = {
"user": os.getenv("DB_USER", "filwords"),
"password": os.environ["DB_PASSWORD"],
"database": os.getenv("DB_NAME", "filwords"),
"host": os.getenv("DB_HOST", "localhost"),
"port": int(os.getenv("DB_PORT", "5432")),
}
ADMIN_IDS = [int(id_str) for id_str in os.getenv("ADMIN_IDS", "6694989403").split(",") if id_str]
FRONTEND_ORIGIN = os.getenv("FRONTEND_ORIGIN", "https://filwordspl.com")
# 🔧 ИСПРАВЛЕНО: единое значение — 24 часа.
# Раньше в auth.py было локально 24 часа, а здесь 3600 (1 час) — это был конфликт.
# Теперь значение берётся ТОЛЬКО отсюда.
MAX_INITDATA_AGE = 24 * 60 * 60 # 24 часа в секундах
SUPPORTED_LANGUAGES = {"pl", "ru", "uk", "en"}
# ============ 🔧 НОВОЕ (18.09.2026): настройки для FeedModal ============
# Папка для хранения картинок feed-постов.
# Путь совпадает с backend/, файлы доступны как static mounts (см. main.py при необходимости).
UPLOADS_DIR = os.getenv("UPLOADS_DIR", "/root/filwordspl/backend/uploads")
# Автосоздание папки — безопасно запускать повторно (exist_ok=True)
os.makedirs(UPLOADS_DIR, exist_ok=True)
# Секции карусели (совпадают с SECTION_CONFIG в FeedModal.tsx)
FEED_SECTIONS = {"news", "partners", "contacts", "apps"}
# Лимит картинок на один пост (совпадает с описанием — до 3)
MAX_FEED_ATTACHMENTS = 3
# Лимит размера одной картинки — 5 МБ
MAX_FEED_IMAGE_SIZE_MB = 5
MAX_FEED_IMAGE_SIZE_BYTES = MAX_FEED_IMAGE_SIZE_MB * 1024 * 1024
# Разрешённые MIME-типы для картинок feed
FEED_ALLOWED_IMAGE_TYPES = {
"image/jpeg",
"image/jpg",
"image/png",
"image/webp",
"image/gif",
}
# Языки, на которые переводим feed-посты (используется в admin_feed translate)
FEED_TARGET_LANGUAGES = ["pl", "ru", "uk", "en"]
+870
View File
@@ -0,0 +1,870 @@
import asyncpg
import logging
from config import DB_CONFIG
logger = logging.getLogger(__name__)
db_pool = None
async def get_pool():
global db_pool
if db_pool is None:
db_pool = await asyncpg.create_pool(**DB_CONFIG, min_size=2, max_size=10)
return db_pool
async def init_db():
pool = await get_pool()
async with pool.acquire() as conn:
# ============ users ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
telegram_id BIGINT UNIQUE NOT NULL,
username TEXT,
app_language TEXT DEFAULT 'pl',
current_level TEXT DEFAULT 'A1',
last_completed_level INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW()
)
""")
# ============ Авто-миграция: добавляем current_level в users ============
col_exists = await conn.fetchval("""
SELECT 1 FROM information_schema.columns
WHERE table_name = 'users' AND column_name = 'current_level'
""")
if not col_exists:
await conn.execute("""
ALTER TABLE users
ADD COLUMN current_level TEXT DEFAULT 'A1'
""")
logger.info("Added current_level column to users")
# ============ levels ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS levels (
id SERIAL PRIMARY KEY,
level_number INTEGER NOT NULL,
grid_size INTEGER NOT NULL,
game_type TEXT DEFAULT 'wordsearch',
level TEXT DEFAULT 'A1',
grid_letters JSONB NOT NULL DEFAULT '[]',
word_paths JSONB NOT NULL DEFAULT '{}'
)
""")
# ============ Убираем старый UNIQUE constraint с levels.level_number ============
await conn.execute("""
ALTER TABLE levels DROP CONSTRAINT IF EXISTS levels_level_number_key
""")
# ============ Добавляем составной UNIQUE (level, level_number) ============
await conn.execute("""
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'levels_level_number_unique'
) THEN
ALTER TABLE levels ADD CONSTRAINT levels_level_number_unique UNIQUE (level, level_number);
END IF;
END $$;
""")
# ============ level_words ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS level_words (
id SERIAL PRIMARY KEY,
level_id INTEGER REFERENCES levels(id) ON DELETE CASCADE,
word_pl TEXT NOT NULL,
translation_ru TEXT DEFAULT '',
translation_uk TEXT DEFAULT '',
translation_en TEXT DEFAULT '',
level TEXT DEFAULT 'A1'
)
""")
# ============ Авто-миграция: добавляем level в level_words ============
col_exists = await conn.fetchval("""
SELECT 1 FROM information_schema.columns
WHERE table_name = 'level_words' AND column_name = 'level'
""")
if not col_exists:
await conn.execute("""
ALTER TABLE level_words
ADD COLUMN level TEXT DEFAULT 'A1'
""")
logger.info("Added level column to level_words")
# ============ user_progress ============
existing = await conn.fetchrow("""
SELECT data_type
FROM information_schema.columns
WHERE table_name = 'user_progress' AND column_name = 'user_id'
""")
if existing and existing['data_type'] != 'bigint':
logger.info("Migrating user_progress.user_id from INTEGER to BIGINT")
await conn.execute("""
ALTER TABLE user_progress
DROP CONSTRAINT IF EXISTS user_progress_pkey CASCADE
""")
await conn.execute("""
ALTER TABLE user_progress
DROP CONSTRAINT IF EXISTS user_progress_user_id_fkey
""")
await conn.execute("""
ALTER TABLE user_progress
ALTER COLUMN user_id TYPE BIGINT
""")
await conn.execute("""
ALTER TABLE user_progress
ADD PRIMARY KEY (user_id, level_id)
""")
await conn.execute("""
ALTER TABLE user_progress
ADD FOREIGN KEY (user_id) REFERENCES users(telegram_id) ON DELETE CASCADE
""")
elif not existing:
await conn.execute("""
CREATE TABLE IF NOT EXISTS user_progress (
user_id BIGINT REFERENCES users(telegram_id) ON DELETE CASCADE,
level_id INTEGER REFERENCES levels(id) ON DELETE CASCADE,
completed_at TIMESTAMP,
hints_used INTEGER DEFAULT 0,
score INTEGER DEFAULT 0,
level TEXT DEFAULT 'A1',
PRIMARY KEY (user_id, level_id)
)
""")
# ============ Авто-миграция: добавляем level в user_progress ============
col_exists = await conn.fetchval("""
SELECT 1 FROM information_schema.columns
WHERE table_name = 'user_progress' AND column_name = 'level'
""")
if not col_exists:
await conn.execute("""
ALTER TABLE user_progress
ADD COLUMN level TEXT DEFAULT 'A1'
""")
logger.info("Added level column to user_progress")
# ============ sentences ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS sentences (
id SERIAL PRIMARY KEY,
sentence_number INTEGER NOT NULL,
sentence_pl TEXT NOT NULL,
translation_ru TEXT DEFAULT '',
translation_uk TEXT DEFAULT '',
translation_en TEXT DEFAULT '',
difficulty INTEGER DEFAULT 1,
level TEXT DEFAULT 'A1'
)
""")
# ============ Убираем старый UNIQUE constraint с sentences.sentence_number ============
await conn.execute("""
ALTER TABLE sentences DROP CONSTRAINT IF EXISTS sentences_sentence_number_key
""")
# ============ Добавляем составной UNIQUE (level, sentence_number) ============
await conn.execute("""
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'sentences_sentence_number_unique'
) THEN
ALTER TABLE sentences ADD CONSTRAINT sentences_sentence_number_unique UNIQUE (level, sentence_number);
END IF;
END $$;
""")
# ============ sentence_progress ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS sentence_progress (
user_id BIGINT REFERENCES users(telegram_id) ON DELETE CASCADE,
sentence_id INTEGER REFERENCES sentences(id) ON DELETE CASCADE,
completed_at TIMESTAMP,
score INTEGER DEFAULT 0,
level TEXT DEFAULT 'A1',
PRIMARY KEY (user_id, sentence_id)
)
""")
# ============ Авто-миграция: добавляем level в sentence_progress ============
col_exists = await conn.fetchval("""
SELECT 1 FROM information_schema.columns
WHERE table_name = 'sentence_progress' AND column_name = 'level'
""")
if not col_exists:
await conn.execute("""
ALTER TABLE sentence_progress
ADD COLUMN level TEXT DEFAULT 'A1'
""")
logger.info("Added level column to sentence_progress")
# ============ typing_progress ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS typing_progress (
user_id BIGINT REFERENCES users(telegram_id) ON DELETE CASCADE,
sentence_id INTEGER REFERENCES sentences(id) ON DELETE CASCADE,
completed_at TIMESTAMP DEFAULT NOW(),
PRIMARY KEY (user_id, sentence_id)
)
""")
# ============ Индекс для typing_progress ============
await conn.execute("CREATE INDEX IF NOT EXISTS idx_typing_progress_user ON typing_progress(user_id)")
# ============ chat_history ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS chat_history (
id SERIAL PRIMARY KEY,
user_id BIGINT REFERENCES users(telegram_id) ON DELETE CASCADE,
message TEXT NOT NULL,
is_user BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT NOW()
)
""")
# ============ Индекс для chat_history ============
await conn.execute("CREATE INDEX IF NOT EXISTS idx_chat_history_user ON chat_history(user_id, created_at DESC)")
# ============ chat_usage ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS chat_usage (
id SERIAL PRIMARY KEY,
user_id BIGINT REFERENCES users(telegram_id) ON DELETE CASCADE,
usage_date DATE DEFAULT CURRENT_DATE,
message_count INTEGER DEFAULT 0,
UNIQUE (user_id, usage_date)
)
""")
# ============ Индекс для chat_usage ============
await conn.execute("CREATE INDEX IF NOT EXISTS idx_chat_usage_user ON chat_usage(user_id, usage_date DESC)")
# ============ learning_levels ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS learning_levels (
id SERIAL PRIMARY KEY,
code VARCHAR(10) UNIQUE NOT NULL,
name_pl TEXT NOT NULL,
name_ru TEXT NOT NULL,
name_uk TEXT NOT NULL,
name_en TEXT NOT NULL,
sort_order INT NOT NULL,
is_active BOOLEAN DEFAULT TRUE
)
""")
# ============ learning_topics ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS learning_topics (
id SERIAL PRIMARY KEY,
topic_id VARCHAR(100) UNIQUE NOT NULL,
level_code VARCHAR(10) NOT NULL,
order_number INT NOT NULL,
name_pl TEXT NOT NULL,
name_ru TEXT NOT NULL,
name_uk TEXT NOT NULL,
name_en TEXT NOT NULL,
description_pl TEXT,
description_ru TEXT,
description_uk TEXT,
description_en TEXT,
is_active BOOLEAN DEFAULT TRUE,
UNIQUE(level_code, order_number)
)
""")
# ============ learning_lessons ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS learning_lessons (
id SERIAL PRIMARY KEY,
lesson_id VARCHAR(120) UNIQUE NOT NULL,
topic_id VARCHAR(100) NOT NULL REFERENCES learning_topics(topic_id),
order_number INT NOT NULL,
skill_tag VARCHAR(100) NOT NULL,
title_pl TEXT NOT NULL,
title_ru TEXT NOT NULL,
title_uk TEXT NOT NULL,
title_en TEXT NOT NULL,
objective_pl TEXT,
objective_ru TEXT,
objective_uk TEXT,
objective_en TEXT,
explanation_pl TEXT,
explanation_ru TEXT,
explanation_uk TEXT,
explanation_en TEXT,
is_active BOOLEAN DEFAULT TRUE,
UNIQUE(topic_id, order_number)
)
""")
# ============ lesson_examples ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS lesson_examples (
id SERIAL PRIMARY KEY,
lesson_id VARCHAR(120) NOT NULL REFERENCES learning_lessons(lesson_id),
order_number INT NOT NULL,
text_pl TEXT NOT NULL,
text_ru TEXT,
text_uk TEXT,
text_en TEXT,
audio_url TEXT
)
""")
# ============ learning_questions ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS learning_questions (
id SERIAL PRIMARY KEY,
question_id VARCHAR(150) UNIQUE NOT NULL,
lesson_id VARCHAR(120) REFERENCES learning_lessons(lesson_id),
topic_id VARCHAR(100) NOT NULL REFERENCES learning_topics(topic_id),
context_type VARCHAR(50) NOT NULL,
question_type VARCHAR(50) NOT NULL,
skill_tag VARCHAR(100) NOT NULL,
difficulty INT NOT NULL CHECK (difficulty BETWEEN 1 AND 3),
prompt_pl TEXT,
prompt_ru TEXT,
prompt_uk TEXT,
prompt_en TEXT,
correct_answer TEXT,
hint_type VARCHAR(50),
hint_value TEXT,
is_test_question BOOLEAN DEFAULT FALSE,
test_order INT,
options_language TEXT DEFAULT 'pl'
)
""")
# ============ Авто-миграция: добавляем options_language ============
col_exists = await conn.fetchval("""
SELECT 1 FROM information_schema.columns
WHERE table_name = 'learning_questions' AND column_name = 'options_language'
""")
if not col_exists:
await conn.execute("""
ALTER TABLE learning_questions
ADD COLUMN options_language TEXT DEFAULT 'pl'
""")
logger.info("Added options_language column to learning_questions")
# ============ question_options ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS question_options (
id SERIAL PRIMARY KEY,
question_id VARCHAR(150) NOT NULL REFERENCES learning_questions(question_id),
option_key VARCHAR(20) NOT NULL,
text_pl TEXT,
text_ru TEXT,
text_uk TEXT,
text_en TEXT,
is_correct BOOLEAN NOT NULL,
wrong_because VARCHAR(100),
UNIQUE(question_id, option_key)
)
""")
# ============ learning_sessions ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS learning_sessions (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
level_code VARCHAR(10) NOT NULL,
topic_id VARCHAR(100) NOT NULL,
session_type VARCHAR(30) NOT NULL,
started_at TIMESTAMP NOT NULL DEFAULT NOW(),
completed_at TIMESTAMP,
is_completed BOOLEAN DEFAULT FALSE
)
""")
# ============ user_learning_answers ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS user_learning_answers (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
session_id BIGINT NOT NULL REFERENCES learning_sessions(id),
level_code VARCHAR(10) NOT NULL,
topic_id VARCHAR(100) NOT NULL,
lesson_id VARCHAR(120),
question_id VARCHAR(150) NOT NULL,
selected_answer TEXT,
correct BOOLEAN,
skill_tag VARCHAR(100) NOT NULL,
question_type VARCHAR(50) NOT NULL,
context_type VARCHAR(50) NOT NULL,
difficulty INT NOT NULL,
time_taken_ms INT,
attempt_number INT DEFAULT 1,
hint_used BOOLEAN DEFAULT FALSE,
wrong_because VARCHAR(100),
is_final_answer BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT NOW()
)
""")
# ============ user_lesson_progress ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS user_lesson_progress (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
lesson_id VARCHAR(120) NOT NULL,
started_at TIMESTAMP,
completed_at TIMESTAMP,
attempts_count INT DEFAULT 0,
correct_count INT DEFAULT 0,
best_score NUMERIC(5,2),
average_time_ms INT,
completed BOOLEAN DEFAULT FALSE,
UNIQUE(user_id, lesson_id)
)
""")
# ============ user_topic_progress ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS user_topic_progress (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
topic_id VARCHAR(100) NOT NULL,
lessons_total INT DEFAULT 0,
lessons_completed INT DEFAULT 0,
test_started BOOLEAN DEFAULT FALSE,
test_completed BOOLEAN DEFAULT FALSE,
test_score NUMERIC(5,2),
topic_mastery NUMERIC(5,2),
confidence NUMERIC(5,2),
completed_at TIMESTAMP,
UNIQUE(user_id, topic_id)
)
""")
# ============ user_skill_mastery (Analytics 2.0 + Этап 2) ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS user_skill_mastery (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
skill_tag VARCHAR(100) NOT NULL,
attempts_count INT DEFAULT 0,
correct_count INT DEFAULT 0,
recognition_attempts INT DEFAULT 0,
recognition_correct INT DEFAULT 0,
recognition_score NUMERIC(5,2),
production_attempts INT DEFAULT 0,
production_correct INT DEFAULT 0,
production_score NUMERIC(5,2),
application_attempts INT DEFAULT 0,
application_correct INT DEFAULT 0,
application_score NUMERIC(5,2),
retention_score NUMERIC(5,2) DEFAULT 0,
hint_dependency NUMERIC(5,2) DEFAULT 0,
average_time_ms INT DEFAULT 0,
median_time_ms INT DEFAULT 0,
median_correct_time_ms INT DEFAULT 0,
median_wrong_time_ms INT DEFAULT 0,
recent_accuracy NUMERIC(5,2) DEFAULT 0,
last_3_accuracy NUMERIC(5,2),
last_5_accuracy NUMERIC(5,2),
last_10_accuracy NUMERIC(5,2),
long_term_accuracy NUMERIC(5,2) DEFAULT 0,
mastery_score NUMERIC(5,2) DEFAULT 0,
previous_mastery_score NUMERIC(5,2) DEFAULT 0,
mastery_delta NUMERIC(5,2) DEFAULT 0,
trend TEXT DEFAULT 'insufficient_data',
retention_status TEXT DEFAULT 'fresh',
fluency_score NUMERIC(5,2) DEFAULT 0,
passive_knowledge_score NUMERIC(5,2) DEFAULT 0,
active_knowledge_score NUMERIC(5,2) DEFAULT 0,
confidence NUMERIC(5,2) DEFAULT 0,
error_count INT DEFAULT 0,
repeated_error_count INT DEFAULT 0,
days_since_last_attempt INT DEFAULT 0,
last_attempt_at TIMESTAMP,
UNIQUE(user_id, skill_tag)
)
""")
# ============ Авто-миграции для user_skill_mastery (Этап 2) ============
stage2_columns = {
"previous_mastery_score": "NUMERIC(5,2) DEFAULT 0",
"mastery_delta": "NUMERIC(5,2) DEFAULT 0",
"trend": "TEXT DEFAULT 'insufficient_data'",
"retention_status": "TEXT DEFAULT 'fresh'",
"fluency_score": "NUMERIC(5,2) DEFAULT 0",
"passive_knowledge_score": "NUMERIC(5,2) DEFAULT 0",
"active_knowledge_score": "NUMERIC(5,2) DEFAULT 0",
}
for col_name, col_type in stage2_columns.items():
col_exists = await conn.fetchval("""
SELECT 1 FROM information_schema.columns
WHERE table_name = 'user_skill_mastery' AND column_name = $1
""", col_name)
if not col_exists:
await conn.execute(f"""
ALTER TABLE user_skill_mastery
ADD COLUMN {col_name} {col_type}
""")
logger.info(f"Added {col_name} column to user_skill_mastery")
# ============ ai_user_context ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS ai_user_context (
user_id BIGINT PRIMARY KEY,
current_level VARCHAR(10),
completed_topics INT DEFAULT 0,
level_topics_total INT DEFAULT 0,
level_completion_percent NUMERIC(5,2) DEFAULT 0,
completed_lessons INT DEFAULT 0,
overall_mastery NUMERIC(5,2) DEFAULT 0,
strong_skills JSONB DEFAULT '[]',
weak_skills JSONB DEFAULT '[]',
uncertain_skills JSONB DEFAULT '[]',
recurring_errors JSONB DEFAULT '[]',
recommended_topics JSONB DEFAULT '[]',
recommended_lessons JSONB DEFAULT '[]',
last_analysis_at TIMESTAMP,
analytics_enabled BOOLEAN DEFAULT FALSE
)
""")
# ============ Авто-миграция: добавляем level_topics_total ============
col_exists = await conn.fetchval("""
SELECT 1 FROM information_schema.columns
WHERE table_name = 'ai_user_context' AND column_name = 'level_topics_total'
""")
if not col_exists:
await conn.execute("""
ALTER TABLE ai_user_context
ADD COLUMN level_topics_total INT DEFAULT 0
""")
logger.info("Added level_topics_total column to ai_user_context")
# ============ Авто-миграция: добавляем level_completion_percent ============
col_exists = await conn.fetchval("""
SELECT 1 FROM information_schema.columns
WHERE table_name = 'ai_user_context' AND column_name = 'level_completion_percent'
""")
if not col_exists:
await conn.execute("""
ALTER TABLE ai_user_context
ADD COLUMN level_completion_percent NUMERIC(5,2) DEFAULT 0
""")
logger.info("Added level_completion_percent column to ai_user_context")
# ============ achievements ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS achievements (
id SERIAL PRIMARY KEY,
code TEXT UNIQUE NOT NULL,
category TEXT DEFAULT 'general',
name_pl TEXT NOT NULL,
name_ru TEXT NOT NULL,
name_uk TEXT NOT NULL,
name_en TEXT NOT NULL,
description_pl TEXT DEFAULT '',
description_ru TEXT DEFAULT '',
description_uk TEXT DEFAULT '',
description_en TEXT DEFAULT '',
icon TEXT DEFAULT '🏆',
requirement_type TEXT NOT NULL,
requirement_value INTEGER DEFAULT 0
)
""")
# ============ user_achievements ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS user_achievements (
user_id BIGINT REFERENCES users(telegram_id) ON DELETE CASCADE,
achievement_id INTEGER REFERENCES achievements(id) ON DELETE CASCADE,
unlocked_at TIMESTAMP DEFAULT NOW(),
PRIMARY KEY (user_id, achievement_id)
)
""")
# ============ daily_logins ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS daily_logins (
user_id BIGINT REFERENCES users(telegram_id) ON DELETE CASCADE,
login_date DATE DEFAULT CURRENT_DATE,
PRIMARY KEY (user_id, login_date)
)
""")
# ============ word_quiz_progress ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS word_quiz_progress (
user_id BIGINT REFERENCES users(telegram_id) ON DELETE CASCADE,
words_guessed INTEGER DEFAULT 0,
total_score INTEGER DEFAULT 0,
PRIMARY KEY (user_id)
)
""")
# ============ Авто-миграция: добавляем correct_streak ============
col_exists = await conn.fetchval("""
SELECT 1 FROM information_schema.columns
WHERE table_name = 'word_quiz_progress' AND column_name = 'correct_streak'
""")
if not col_exists:
await conn.execute("""
ALTER TABLE word_quiz_progress
ADD COLUMN correct_streak INTEGER DEFAULT 0
""")
logger.info("Added correct_streak column to word_quiz_progress")
# ============ Авто-миграция: добавляем recent_words ============
col_exists = await conn.fetchval("""
SELECT 1 FROM information_schema.columns
WHERE table_name = 'word_quiz_progress' AND column_name = 'recent_words'
""")
if not col_exists:
await conn.execute("""
ALTER TABLE word_quiz_progress
ADD COLUMN recent_words TEXT[] DEFAULT '{}'
""")
logger.info("Added recent_words column to word_quiz_progress")
# ============ Авто-миграция: добавляем level в word_quiz_progress ============
col_exists = await conn.fetchval("""
SELECT 1 FROM information_schema.columns
WHERE table_name = 'word_quiz_progress' AND column_name = 'level'
""")
if not col_exists:
await conn.execute("""
ALTER TABLE word_quiz_progress
ADD COLUMN level TEXT DEFAULT 'A1'
""")
logger.info("Added level column to word_quiz_progress")
# ============ bubble_words_progress ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS bubble_words_progress (
user_id BIGINT REFERENCES users(telegram_id) ON DELETE CASCADE,
levels_completed INTEGER DEFAULT 0,
total_score INTEGER DEFAULT 0,
PRIMARY KEY (user_id)
)
""")
# ============ Авто-миграция: добавляем level в bubble_words_progress ============
col_exists = await conn.fetchval("""
SELECT 1 FROM information_schema.columns
WHERE table_name = 'bubble_words_progress' AND column_name = 'level'
""")
if not col_exists:
await conn.execute("""
ALTER TABLE bubble_words_progress
ADD COLUMN level TEXT DEFAULT 'A1'
""")
logger.info("Added level column to bubble_words_progress")
# ============ wrong_because_explanations ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS wrong_because_explanations (
id SERIAL PRIMARY KEY,
wrong_because VARCHAR(100) UNIQUE NOT NULL,
explanation_pl TEXT NOT NULL,
explanation_ru TEXT NOT NULL,
explanation_uk TEXT NOT NULL,
explanation_en TEXT NOT NULL
)
""")
# ============ teacher_reactions ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS teacher_reactions (
id SERIAL PRIMARY KEY,
reaction_type VARCHAR(50) NOT NULL,
text_pl TEXT NOT NULL,
text_ru TEXT NOT NULL,
text_uk TEXT NOT NULL,
text_en TEXT NOT NULL
)
""")
# ============ Индекс для teacher_reactions ============
await conn.execute("CREATE INDEX IF NOT EXISTS idx_teacher_reactions_type ON teacher_reactions(reaction_type)")
# ============ teacher_chat_history ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS teacher_chat_history (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL UNIQUE,
messages JSONB NOT NULL DEFAULT '[]',
updated_at TIMESTAMP DEFAULT NOW()
)
""")
# ============ teacher_ai_usage ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS teacher_ai_usage (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
usage_date DATE DEFAULT CURRENT_DATE,
command_count INTEGER DEFAULT 0,
UNIQUE(user_id, usage_date)
)
""")
# ============ daily_phrases — фраза дня ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS daily_phrases (
id SERIAL PRIMARY KEY,
phrase_date DATE UNIQUE NOT NULL,
phrase_pl TEXT NOT NULL,
translations JSONB NOT NULL DEFAULT '{}',
grammar_breakdowns JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMP DEFAULT NOW()
)
""")
# ============ Индекс для daily_phrases ============
await conn.execute("CREATE INDEX IF NOT EXISTS idx_daily_phrases_date ON daily_phrases(phrase_date DESC)")
# ============ СИСТЕМА ПОДДЕРЖКИ ============
# ============ support_tickets ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS support_tickets (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
status TEXT DEFAULT 'open',
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
)
""")
# ============ Индекс для support_tickets ============
await conn.execute("CREATE INDEX IF NOT EXISTS idx_support_tickets_user ON support_tickets(user_id)")
await conn.execute("CREATE INDEX IF NOT EXISTS idx_support_tickets_status ON support_tickets(status)")
# ============ support_messages ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS support_messages (
id BIGSERIAL PRIMARY KEY,
ticket_id BIGINT NOT NULL REFERENCES support_tickets(id) ON DELETE CASCADE,
sender_id BIGINT NOT NULL,
message TEXT NOT NULL,
is_read BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT NOW()
)
""")
# ============ Индекс для support_messages ============
await conn.execute("CREATE INDEX IF NOT EXISTS idx_support_messages_ticket ON support_messages(ticket_id)")
await conn.execute("CREATE INDEX IF NOT EXISTS idx_support_messages_sender ON support_messages(sender_id)")
await conn.execute("CREATE INDEX IF NOT EXISTS idx_support_messages_unread ON support_messages(ticket_id, is_read)")
# ============ 🔧 НОВОЕ (18.09.2026): feed_posts — посты для карусели ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS feed_posts (
id BIGSERIAL PRIMARY KEY,
section TEXT NOT NULL,
source_language TEXT NOT NULL DEFAULT 'ru',
title_pl TEXT,
title_ru TEXT,
title_uk TEXT,
title_en TEXT,
body_pl TEXT,
body_ru TEXT,
body_uk TEXT,
body_en TEXT,
link_url TEXT,
telegram_url TEXT,
author_id BIGINT NOT NULL,
is_pinned BOOLEAN DEFAULT FALSE,
is_published BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
edited_at TIMESTAMP,
expires_at TIMESTAMP
)
""")
# ============ Индексы для feed_posts ============
await conn.execute("""
CREATE INDEX IF NOT EXISTS idx_feed_posts_section
ON feed_posts(section, created_at DESC)
""")
await conn.execute("""
CREATE INDEX IF NOT EXISTS idx_feed_posts_pinned
ON feed_posts(section, is_pinned DESC, created_at DESC)
""")
await conn.execute("""
CREATE INDEX IF NOT EXISTS idx_feed_posts_published
ON feed_posts(section, is_published)
""")
# ============ 🔧 НОВОЕ (18.09.2026): feed_attachments — картинки к постам (до 3) ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS feed_attachments (
id BIGSERIAL PRIMARY KEY,
post_id BIGINT NOT NULL REFERENCES feed_posts(id) ON DELETE CASCADE,
file_path TEXT NOT NULL,
file_type TEXT DEFAULT 'image',
order_number INT NOT NULL DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW()
)
""")
# ============ Индекс для feed_attachments ============
await conn.execute("""
CREATE INDEX IF NOT EXISTS idx_feed_attachments_post
ON feed_attachments(post_id, order_number)
""")
# ============ user_learning_answers_archive ============
await conn.execute("""
CREATE TABLE IF NOT EXISTS user_learning_answers_archive (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
skill_tag VARCHAR(100) NOT NULL,
correct BOOLEAN,
wrong_because VARCHAR(100),
created_at DATE,
archived_at TIMESTAMP DEFAULT NOW()
)
""")
# ============ Индексы для архива ============
await conn.execute("CREATE INDEX IF NOT EXISTS idx_archive_user_date ON user_learning_answers_archive(user_id, created_at)")
await conn.execute("CREATE INDEX IF NOT EXISTS idx_archive_skill_tag ON user_learning_answers_archive(skill_tag)")
# ============ Индексы ============
await conn.execute("CREATE INDEX IF NOT EXISTS idx_levels_number ON levels(level_number)")
await conn.execute("CREATE INDEX IF NOT EXISTS idx_levels_level ON levels(level)")
await conn.execute("CREATE INDEX IF NOT EXISTS idx_levels_level_number ON levels(level, level_number)")
await conn.execute("CREATE INDEX IF NOT EXISTS idx_user_progress_user ON user_progress(user_id)")
await conn.execute("CREATE INDEX IF NOT EXISTS idx_level_words_level_id ON level_words(level_id)")
await conn.execute("CREATE INDEX IF NOT EXISTS idx_level_words_level ON level_words(level)")
await conn.execute("CREATE INDEX IF NOT EXISTS idx_users_telegram ON users(telegram_id)")
await conn.execute("CREATE INDEX IF NOT EXISTS idx_sentences_number ON sentences(sentence_number)")
await conn.execute("CREATE INDEX IF NOT EXISTS idx_sentences_level ON sentences(level)")
await conn.execute("CREATE INDEX IF NOT EXISTS idx_sentences_level_number ON sentences(level, sentence_number)")
await conn.execute("CREATE INDEX IF NOT EXISTS idx_sentence_progress_user ON sentence_progress(user_id)")
await conn.execute("CREATE INDEX IF NOT EXISTS idx_typing_progress_user ON typing_progress(user_id)")
await conn.execute("CREATE INDEX IF NOT EXISTS idx_user_progress_score ON user_progress(score DESC)")
await conn.execute("CREATE INDEX IF NOT EXISTS idx_sentence_progress_score ON sentence_progress(score DESC)")
await conn.execute("CREATE INDEX IF NOT EXISTS idx_user_achievements_user ON user_achievements(user_id)")
await conn.execute("CREATE INDEX IF NOT EXISTS idx_daily_logins_user ON daily_logins(user_id)")
await conn.execute("CREATE INDEX IF NOT EXISTS idx_daily_logins_date ON daily_logins(login_date DESC)")
await conn.execute("CREATE INDEX IF NOT EXISTS idx_learning_topics_level ON learning_topics(level_code)")
await conn.execute("CREATE INDEX IF NOT EXISTS idx_learning_lessons_topic ON learning_lessons(topic_id)")
await conn.execute("CREATE INDEX IF NOT EXISTS idx_learning_questions_lesson ON learning_questions(lesson_id)")
await conn.execute("CREATE INDEX IF NOT EXISTS idx_learning_questions_topic ON learning_questions(topic_id)")
await conn.execute("CREATE INDEX IF NOT EXISTS idx_learning_answers_user ON user_learning_answers(user_id, created_at DESC)")
await conn.execute("CREATE INDEX IF NOT EXISTS idx_skill_mastery_user ON user_skill_mastery(user_id)")
logger.info("Database initialized")
+55
View File
@@ -0,0 +1,55 @@
#!/bin/bash
# Собирает весь backend в один .md файл
PROJECT_DIR="/root/filwordspl/backend"
OUTPUT="/root/FILWORDS_BACKEND_$(date +%Y%m%d_%H%M).md"
{
echo "# 🐍 Filwords PL — Backend"
echo ""
echo "**Дата:** $(date)"
echo "**Путь:** $PROJECT_DIR"
echo ""
# Все .py файлы (кроме venv и __pycache__)
for f in $(find "$PROJECT_DIR" -type f -name "*.py" -not -path "*/venv/*" -not -path "*/__pycache__/*" | sort); do
REL_PATH="${f#$PROJECT_DIR/}"
echo "### \`$REL_PATH\`"
echo '```python'
cat "$f"
echo '```'
echo ""
done
# Конфиги
for f in "$PROJECT_DIR/requirements.txt" "$PROJECT_DIR/pytest.ini" "$PROJECT_DIR/.gitignore"; do
if [ -f "$f" ]; then
REL_PATH="${f#$PROJECT_DIR/}"
echo "### \`$REL_PATH\`"
echo '```'
cat "$f"
echo '```'
echo ""
fi
done
# Тесты
if [ -d "$PROJECT_DIR/tests" ]; then
for f in $(find "$PROJECT_DIR/tests" -type f -name "*.py" | sort); do
REL_PATH="${f#$PROJECT_DIR/}"
echo "### \`$REL_PATH\`"
echo '```python'
cat "$f"
echo '```'
echo ""
done
fi
} > "$OUTPUT"
echo "🔍 Проверка секретов (должно быть 0):"
grep -c -E "(sk-[a-zA-Z0-9]{20,}|postgresql://[^ ]*:[^ ]*@|BOT_TOKEN\s*=\s*[\"'][0-9]|password\s*=\s*[\"'][^\"']{4,})" "$OUTPUT" || echo "0"
echo "✅ Готово: $OUTPUT"
echo "📊 Размер: $(du -h "$OUTPUT" | cut -f1)"
echo "📏 Строк: $(wc -l < "$OUTPUT")"
+43
View File
@@ -0,0 +1,43 @@
with open('routers/learning.py', 'r') as f:
content = f.read()
# Добавляем regex для удаления технических ID
content = content.replace(
''' data = response.json()
ai_response = data["choices"][0]["message"]["content"].strip()
ai_response = ai_response.replace("**", "").replace("*", "").replace("#", "")
return {"analytics_enabled": True, "response": ai_response}''',
''' data = response.json()
ai_response = data["choices"][0]["message"]["content"].strip()
ai_response = ai_response.replace("**", "").replace("*", "").replace("#", "")
# Убираем технические ID в скобках: (self_intro), (family_masz_mam) и т.д.
import re
ai_response = re.sub(r'\\s*\\([a-z_]+_[a-z_]+\\)', '', ai_response)
return {"analytics_enabled": True, "response": ai_response}'''
)
# Убираем из промпта упоминание процента завершения уровня (правило 12)
content = content.replace(
'''12. ВАЖНО: level_completion_percent показывает сколько тем пройдено из ВСЕГО уровня (A1 = 30+ тем). Если пройдено 5 тем из 30+ — это только начало (около 16%), а не "почти готово". НЕ говори что уровень почти завершён, если пройдено меньше 100%.
''',
'''12. ВАЖНО: НИКОГДА не упоминай процент завершения уровня, количество пройденных тем или сколько тем осталось. Не говори "ты прошёл X тем" или "осталось Y тем". Просто анализируй навыки.
'''
)
# Убираем из user_prompt пункт про прогресс
content = content.replace(
'''5. Твой общий прогресс и мотивация
В конце напиши: "Жду тебя в разделе Повторение!\\""""''',
'''5. Твоя мотивация (без упоминания процентов и количества тем)
В конце напиши: "Жду тебя в разделе Повторение!\\""""'''
)
with open('routers/learning.py', 'w') as f:
f.write(content)
print("learning.py fixed!")
+135
View File
@@ -0,0 +1,135 @@
"""
Патч: исправляет колонку text_pl в question_options там, где при заполнении
контента по ошибке продублировали русский перевод вместо настоящей польской
фразы (вопросы типа "Co oznacza / Co znaczy ...?").
Затронуто 12 question_id по темам: greetings, family, food, home,
numbers_time (3 шт.), colors_clothes, weather_seasons.
Запуск на сервере:
python3 fix_polish_options.py
Требует переменную DB_CONFIG из config.py (как остальные твои скрипты).
Скрипт идемпотентен: можно запускать повторно, ничего не сломает —
он просто перезапишет text_pl тем же корректным значением.
"""
import asyncio
import asyncpg
from config import DB_CONFIG
# Каждая запись: (question_id, option_key, правильный польский текст)
FIXES = [
# ==================== Тема 1: Greetings ====================
("a1_greetings_t09", "A", "Jak się nazywasz?"),
("a1_greetings_t09", "B", "Jak się masz?"),
("a1_greetings_t09", "C", "Gdzie mieszkasz?"),
("a1_greetings_t09", "D", "Do widzenia."),
# ==================== Тема 3: Family ====================
# Один и тот же набор опций встречается в уроке и в тесте
("a1_family_l07_q1", "A", "Masz siostrę?"),
("a1_family_l07_q1", "B", "Czy to jest twoja siostra?"),
("a1_family_l07_q1", "C", "Jesteś siostrą?"),
("a1_family_l07_q1", "D", "Gdzie jest twoja siostra?"),
("a1_family_t06", "A", "Masz siostrę?"),
("a1_family_t06", "B", "Czy to jest twoja siostra?"),
("a1_family_t06", "C", "Jesteś siostrą?"),
("a1_family_t06", "D", "Gdzie jest twoja siostra?"),
# ==================== Тема 4: Food ====================
("a1_food_l08_q1", "A", "Co jesz na śniadanie?"),
("a1_food_l08_q1", "B", "Co pijesz rano?"),
("a1_food_l08_q1", "C", "Kiedy jesz śniadanie?"),
("a1_food_l08_q1", "D", "Czy lubisz śniadanie?"),
# ==================== Тема 5: Home ====================
("a1_home_l09_q1", "A", "Ile masz pokoi?"),
("a1_home_l09_q1", "B", "Gdzie jest twój pokój?"),
("a1_home_l09_q1", "C", "Czy podoba ci się twój pokój?"),
("a1_home_l09_q1", "D", "Jaki jest twój pokój?"),
# ==================== Тема 6: Numbers/Time ====================
("a1_numbers_time_l05_q1", "A", "Która godzina?"),
("a1_numbers_time_l05_q1", "B", "Jaki dziś dzień?"),
("a1_numbers_time_l05_q1", "C", "Ile masz lat?"),
("a1_numbers_time_l05_q1", "D", "Gdzie jesteś?"),
("a1_numbers_time_t04", "A", "Która godzina?"),
("a1_numbers_time_t04", "B", "Jaki dziś dzień?"),
("a1_numbers_time_t04", "C", "Ile masz lat?"),
("a1_numbers_time_t04", "D", "Gdzie jesteś?"),
("a1_numbers_time_l06_q1", "A", "za piętnaście piąta"),
("a1_numbers_time_l06_q1", "B", "kwadrans po czwartej"),
("a1_numbers_time_l06_q1", "C", "wpół do piątej"),
("a1_numbers_time_l06_q1", "D", "dokładnie piąta"),
("a1_numbers_time_l07_q1", "A", "O której godzinie?"),
("a1_numbers_time_l07_q1", "B", "Która godzina?"),
("a1_numbers_time_l07_q1", "C", "Jaki dziś dzień?"),
("a1_numbers_time_l07_q1", "D", "Jak długo?"),
("a1_numbers_time_t05", "A", "O której godzinie?"),
("a1_numbers_time_t05", "B", "Która godzina?"),
("a1_numbers_time_t05", "C", "Jaki dziś dzień?"),
("a1_numbers_time_t05", "D", "Jak długo?"),
# ==================== Тема 7: Colors/Clothes ====================
("a1_colors_clothes_l09_q1", "A", "Jakiego koloru jest...?"),
("a1_colors_clothes_l09_q1", "B", "Która godzina jest...?"),
("a1_colors_clothes_l09_q1", "C", "Ile kosztuje...?"),
("a1_colors_clothes_l09_q1", "D", "Gdzie jest...?"),
("a1_colors_clothes_t06", "A", "Jakiego koloru jest...?"),
("a1_colors_clothes_t06", "B", "Która godzina jest...?"),
("a1_colors_clothes_t06", "C", "Ile kosztuje...?"),
("a1_colors_clothes_t06", "D", "Gdzie jest...?"),
# ==================== Тема 8: Weather/Seasons ====================
("a1_weather_seasons_l09_q1", "A", "Jutro będzie padać deszcz."),
("a1_weather_seasons_l09_q1", "B", "Wczoraj padał deszcz."),
("a1_weather_seasons_l09_q1", "C", "Dziś pada deszcz."),
("a1_weather_seasons_l09_q1", "D", "Jutro będzie zimno."),
("a1_weather_seasons_t06", "A", "Jutro będzie padać deszcz."),
("a1_weather_seasons_t06", "B", "Wczoraj padał deszcz."),
("a1_weather_seasons_t06", "C", "Dziś pada deszcz."),
("a1_weather_seasons_t06", "D", "Jutro będzie zimno."),
]
async def main():
pool = await asyncpg.create_pool(**DB_CONFIG)
updated = 0
not_found = []
async with pool.acquire() as conn:
for question_id, option_key, correct_pl in FIXES:
result = await conn.execute("""
UPDATE question_options
SET text_pl = $3
WHERE question_id = $1 AND option_key = $2
""", question_id, option_key, correct_pl)
# asyncpg возвращает строку вида "UPDATE 1" или "UPDATE 0"
affected = int(result.split()[-1])
if affected == 0:
not_found.append((question_id, option_key))
else:
updated += affected
await pool.close()
print(f"✅ Обновлено записей: {updated} из {len(FIXES)}")
if not_found:
print(f"⚠️ Не найдено в БД (проверь question_id/option_key вручную):")
for qid, key in not_found:
print(f" - {qid} / {key}")
else:
print("🎉 Все записи найдены и обновлены.")
if __name__ == "__main__":
asyncio.run(main())
+113
View File
@@ -0,0 +1,113 @@
import re
with open('routers/review.py', 'r') as f:
content = f.read()
# Полный SKILL_TAG_DESCRIPTION (как в learning.py)
full_skill_tags = '''# Понятные названия навыков
SKILL_TAG_DESCRIPTION = """
Понятные названия навыков (используй ИХ в ответе, а не технические ID):
- family_vocabulary → "слова по теме «Семья»"
- family_possessive → "мой/моя (mój/moja)"
- family_accusative → "винительный падеж (mam brata, mam siostrę)"
- family_questions → "вопросы о семье (Masz brata?)"
- family_description → "рассказ о семье"
- family_babcia_dziadek → "бабушка и дедушка"
- family_brat_siostra → "брат и сестра"
- family_masz_mam → "у меня есть / у тебя есть"
- family_duza_mala → "большая/маленькая семья"
- family_mama_tata → "мама и папа"
- family_maz_zona → "муж и жена"
- family_dzieci → "дети"
- family_wujek_ciocia → "дядя и тётя"
- family_kto_to_jest → "вопрос «Кто это?»"
- asking_name → "вопрос «Как тебя зовут?»"
- answering_name → "ответ на вопрос об имени"
- formal_address_pan_pani → "вежливое обращение Pan/Pani"
- greeting_formal → "формальные приветствия"
- greeting_informal → "неформальные приветствия"
- asking_origin → "вопрос «Откуда ты?»"
- answering_origin → "ответ о происхождении"
- asking_residence → "вопрос «Где ты живёшь?»"
- answering_residence → "ответ о месте жительства"
- self_description → "рассказ о себе"
- self_intro → "представление себя"
- how_are_you → "вопрос «Как дела?»"
- responding_how_are_you → "ответ на «Как дела?»"
- farewell_formal → "формальные прощания"
- farewell_informal → "неформальные прощания"
- mieszkac_conjugation → "спряжение глагола mieszkać"
- skad_vs_gdzie → "разница между Skąd и Gdzie"
- country_vocabulary → "названия стран"
- nationality_forms → "национальности"
- city_vocabulary → "названия городов"
- food_vocabulary → "слова по теме «Еда»"
- food_fruits_vegetables → "фрукты и овощи"
- food_drinks → "напитки"
- food_meals → "приёмы пищи"
- food_lubie → "глагол lubić (любить)"
- home_vocabulary → "слова по теме «Дом»"
- home_furniture → "мебель"
- home_location → "предлоги места (w, na)"
- home_ownership → "mam dom, mam mieszkanie"
- home_description → "описание дома/квартиры"
НИКОГДА не пиши технические ID навыков в ответе. Всегда заменяй их на понятные названия из списка выше.
"""'''
# Заменяем короткий SKILL_TAG_DESCRIPTION на полный
content = re.sub(
r'# Понятные названия навыков\nSKILL_TAG_DESCRIPTION = """.*?"""',
full_skill_tags,
content,
flags=re.DOTALL
)
# Добавляем attempts_count >= 2 в SQL
content = content.replace(
''' WHERE user_id = $1
AND (
mastery_score < 75
OR retention_status IN ('needs_review', 'overdue')
OR repeated_error_count > 0
)''',
''' WHERE user_id = $1
AND attempts_count >= 2
AND (
mastery_score < 75
OR retention_status IN ('needs_review', 'overdue')
OR repeated_error_count > 0
)'''
)
# Добавляем regex для удаления технических ID
content = content.replace(
''' data = response.json()
review_text = data["choices"][0]["message"]["content"].strip()
review_text = review_text.replace("**", "").replace("*", "").replace("#", "")''',
''' data = response.json()
review_text = data["choices"][0]["message"]["content"].strip()
review_text = review_text.replace("**", "").replace("*", "").replace("#", "")
# Убираем технические ID в скобках
import re
review_text = re.sub(r'\\s*\\([a-z_]+_[a-z_]+\\), '', review_text)'''
)
# Добавляем правила в промпт
content = content.replace(
'''5. Каждый мини-урок: правило + 2 примера + 1 задание
6. Не пиши технические ID навыков — используй понятные названия
7. Будь дружелюбным и мотивирующим"""''',
'''5. Каждый мини-урок: правило + 2 примера + 1 задание
6. Не пиши технические ID навыков — используй понятные названия
7. Будь дружелюбным и мотивирующим
8. ВАЖНО: Никогда не давай готовый ответ на задание в том же сообщении. Задание должно оставаться открытым.
9. ВАЖНО: В списках слов-подсказок используй только польские слова, без смешения языков.
10. ВАЖНО: Если mastery >= 75 и навык включён из-за retention_status (needs_review/overdue) — формулируй как "быстрое освежение", а не "давай разберём с нуля"."""'''
)
with open('routers/review.py', 'w') as f:
f.write(content)
print("review.py fixed!")
+85
View File
@@ -0,0 +1,85 @@
"""
Полный сброс прогресса пользователя.
Очищает ВСЕ данные по играм, урокам, чат-учителю, достижениям.
Не трогает: users (остаётся), tts_cache (аудио).
Запуск: python3 full_reset.py
"""
import asyncio
import asyncpg
from config import DB_CONFIG
USER_ID = 6694989403
async def main():
pool = await asyncpg.create_pool(**DB_CONFIG)
async with pool.acquire() as conn:
# Проверяем пользователя
user_exists = await conn.fetchval("""
SELECT 1 FROM users WHERE telegram_id = $1
""", USER_ID)
if not user_exists:
print(f"❌ Пользователь {USER_ID} не найден!")
await pool.close()
return
# Список таблиц для очистки (по user_id)
user_tables = [
"user_progress", # Филворды
"sentence_progress", # Предложения
"typing_progress", # Печать
"word_quiz_progress", # Выбери слово
"bubble_words_progress", # Пузыри слов
"user_learning_answers", # Ответы на уроки
"user_lesson_progress", # Прогресс уроков
"user_topic_progress", # Прогресс тем
"user_skill_mastery", # Мастерство
"learning_sessions", # Сессии обучения
"teacher_chat_history", # История чата учителя
"teacher_ai_usage", # AI-команды
"chat_history", # AI-чат
"chat_usage", # Использование AI
"user_achievements", # Достижения
"daily_logins", # Входы
"ai_user_context", # AI-контекст
]
total_deleted = 0
for table in user_tables:
try:
result = await conn.execute(f"""
DELETE FROM {table} WHERE user_id = $1
""", USER_ID)
affected = int(result.split()[-1])
total_deleted += affected
print(f"🗑️ {table}: удалено {affected}")
except Exception as e:
print(f"⚠️ {table}: {str(e)}")
# Сбрасываем last_completed_level
await conn.execute("""
UPDATE users SET last_completed_level = 0, current_level = 'A1'
WHERE telegram_id = $1
""", USER_ID)
print("✅ last_completed_level = 0, current_level = A1")
# Очищаем архив ответов
try:
result = await conn.execute("""
DELETE FROM user_learning_answers_archive WHERE user_id = $1
""", USER_ID)
print(f"🗑️ user_learning_answers_archive: удалено {result.split()[-1]}")
except Exception as e:
print(f"⚠️ archive: {str(e)}")
await pool.close()
print(f"\n🎉 Готово! Удалено {total_deleted} записей.")
print("Можно начинать тестирование с чистого листа!")
if __name__ == "__main__":
asyncio.run(main())
+200
View File
@@ -0,0 +1,200 @@
"""
Автоматическая генерация фразы дня.
Запускается через cron в 03:00 UTC (= 06:00 Минск).
Что делает:
1. Проверяет, есть ли фраза на сегодня в daily_phrases
2. Если нет — генерирует через AI (MiniMax-M3)
3. Пишет в daily_phrases с phrase_date = CURRENT_DATE
Fallback: если AI упал — пишет запасную фразу.
"""
import asyncio
import asyncpg
import httpx
import json
import logging
import random
from datetime import datetime, timezone
from config import DB_CONFIG, TOGETHER_API_KEY
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
TOGETHER_API_URL = "https://api.together.xyz/v1/chat/completions"
TEACHER_MODEL = "MiniMaxAI/MiniMax-M3"
# Список тем для фразы дня
TOPICS_LIST = [
"приветствие и знакомство",
"семья и друзья",
"еда и напитки",
"покупки и магазины",
"транспорт и путешествия",
"работа и профессии",
"погода и времена года",
"дом и квартира",
"хобби и свободное время",
"здоровье и самочувствие",
"город и местность",
"ежедневная рутина",
]
# Fallback фраза, если AI упал
FALLBACK_PHRASE = "Lepiej późno niż wcale."
FALLBACK_TRANSLATIONS = {
"ru": "Лучше поздно, чем никогда.",
"uk": "Краще пізно, ніж ніколи.",
"en": "Better late than never."
}
FALLBACK_GRAMMAR = {
"pl": "Lepiej - przysłówek w stopniu wyższym. Późno - przysłówek.",
"ru": "Lepiej - наречие в сравнительной степени. Późno - наречие.",
"uk": "Lepiej - прислівник у вищому ступені. Późno - прислівник.",
"en": "Lepiej - adverb in comparative degree. Późno - adverb."
}
async def call_ai(system_prompt: str, user_prompt: str, max_tokens: int = 1000) -> str:
"""Вызывает AI и возвращает ответ."""
if not TOGETHER_API_KEY:
logger.error("TOGETHER_API_KEY not configured")
return ""
try:
async with httpx.AsyncClient(timeout=90.0) as client:
response = await client.post(
TOGETHER_API_URL,
headers={
"Authorization": f"Bearer {TOGETHER_API_KEY}",
"Content-Type": "application/json",
},
json={
"model": TEACHER_MODEL,
"messages": [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt},
],
"max_tokens": max_tokens,
"temperature": 0.7,
},
)
if response.status_code != 200:
logger.error(f"AI API error: {response.status_code}, body={response.text[:200]}")
return ""
data = response.json()
return data["choices"][0]["message"]["content"].strip()
except Exception as e:
logger.error(f"AI call error: {e}")
return ""
async def generate_phrase() -> tuple:
"""
Генерирует фразу дня через AI.
Возвращает (phrase_pl, translations_dict, grammar_dict).
"""
selected_topic = random.choice(TOPICS_LIST)
system_prompt = f"""Ты — учитель польского языка. Создай фразу дня для изучения польского.
Тема фразы: {selected_topic}.
Фраза должна быть уровня A2-B1, полезной в повседневной жизни.
Верни ответ строго в таком формате (без markdown, просто текст):
PHRASE: [фраза на польском]
RU: [перевод на русский]
UK: [перевод на украинский]
EN: [перевод на английский]
PL_GRAMMAR: [грамматический разбор на польском, 1-2 предложения]
RU_GRAMMAR: [грамматический разбор на русском, 1-2 предложения]
UK_GRAMMAR: [грамматический разбор на украинском, 1-2 предложения]
EN_GRAMMAR: [грамматический разбор на английском, 1-2 предложения]"""
user_prompt = f"Создай фразу дня на тему: {selected_topic}."
ai_response = await call_ai(system_prompt, user_prompt, max_tokens=1000)
if not ai_response:
logger.warning("AI returned empty response, using fallback")
return FALLBACK_PHRASE, FALLBACK_TRANSLATIONS, FALLBACK_GRAMMAR
# Парсим ответ
phrase_pl = FALLBACK_PHRASE
translations = FALLBACK_TRANSLATIONS.copy()
grammar_breakdowns = FALLBACK_GRAMMAR.copy()
for line in ai_response.strip().split('\n'):
line = line.strip()
if line.startswith('PHRASE:'):
phrase_pl = line.replace('PHRASE:', '').strip()
elif line.startswith('RU:'):
translations["ru"] = line.replace('RU:', '').strip()
elif line.startswith('UK:'):
translations["uk"] = line.replace('UK:', '').strip()
elif line.startswith('EN:'):
translations["en"] = line.replace('EN:', '').strip()
elif line.startswith('PL_GRAMMAR:'):
grammar_breakdowns["pl"] = line.replace('PL_GRAMMAR:', '').strip()
elif line.startswith('RU_GRAMMAR:'):
grammar_breakdowns["ru"] = line.replace('RU_GRAMMAR:', '').strip()
elif line.startswith('UK_GRAMMAR:'):
grammar_breakdowns["uk"] = line.replace('UK_GRAMMAR:', '').strip()
elif line.startswith('EN_GRAMMAR:'):
grammar_breakdowns["en"] = line.replace('EN_GRAMMAR:', '').strip()
logger.info(f"Generated phrase for topic '{selected_topic}': {phrase_pl[:60]}...")
return phrase_pl, translations, grammar_breakdowns
async def main():
logger.info("=== Начало генерации фразы дня ===")
conn = await asyncpg.connect(**DB_CONFIG)
try:
today = datetime.now(timezone.utc).date()
# Проверяем, есть ли уже фраза на сегодня
existing = await conn.fetchrow("""
SELECT id FROM daily_phrases WHERE phrase_date = $1
""", today)
if existing:
logger.info(f"Phrase for {today} already exists, skipping")
return
# Генерируем новую фразу
logger.info(f"Generating phrase for {today}...")
phrase_pl, translations, grammar_breakdowns = await generate_phrase()
# Пишем в БД
await conn.execute("""
INSERT INTO daily_phrases (phrase_date, phrase_pl, translations, grammar_breakdowns)
VALUES ($1, $2, $3::jsonb, $4::jsonb)
ON CONFLICT (phrase_date) DO UPDATE SET
phrase_pl = EXCLUDED.phrase_pl,
translations = EXCLUDED.translations,
grammar_breakdowns = EXCLUDED.grammar_breakdowns
""", today, phrase_pl,
json.dumps(translations, ensure_ascii=False),
json.dumps(grammar_breakdowns, ensure_ascii=False))
logger.info(f"✅ Phrase for {today} saved: {phrase_pl}")
except Exception as e:
logger.error(f"Error: {e}")
finally:
await conn.close()
logger.info("=== Готово ===")
if __name__ == "__main__":
asyncio.run(main())
+273
View File
@@ -0,0 +1,273 @@
import asyncio
import asyncpg
import json
import random
import csv
from config import DB_CONFIG
DIRECTIONS = [(-1,0),(0,-1),(0,1),(1,0)]
ALPHABET = "AĄBCĆDEĘFGHIJKLŁMNŃOÓPRSŚTUWYZŹŻ"
def load_words_from_csv(filename):
"""Загружает слова из CSV файла без тем. Возвращает список слов и словарь переводов."""
words = []
translations = {}
with open(filename, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
word = row['polish'].strip().lower()
if word and word not in words:
words.append(word)
translations[word] = {
'en': row.get('english', ''),
'ru': row.get('russian', ''),
'uk': row.get('ukrainian', '')
}
return words, translations
def get_words_for_grid(all_words, num_words, grid_size):
"""Выбирает случайные слова подходящей длины."""
max_len = grid_size
candidates = [w for w in all_words if len(w) <= max_len]
if len(candidates) < num_words:
candidates = all_words
random.shuffle(candidates)
return candidates[:num_words]
def try_place_word(word, grid_size, occupied, max_attempts=500):
word = word.upper()
for _ in range(max_attempts):
start = (random.randrange(grid_size), random.randrange(grid_size))
if start in occupied:
continue
path = [start]
visited = {start}
ok = True
for _ in range(len(word) - 1):
r, c = path[-1]
candidates = []
for dr, dc in DIRECTIONS:
nr, nc = r + dr, c + dc
if 0 <= nr < grid_size and 0 <= nc < grid_size:
if (nr, nc) not in occupied and (nr, nc) not in visited:
candidates.append((nr, nc))
if not candidates:
ok = False
break
nxt = random.choice(candidates)
path.append(nxt)
visited.add(nxt)
if ok and len(path) == len(word):
return path
return None
def fill_empty_cells(grid, grid_size, occupied, all_words, used_words):
"""Заполняет пустые клетки короткими словами из того же уровня."""
empty_cells = set()
for r in range(grid_size):
for c in range(grid_size):
if (r, c) not in occupied:
empty_cells.add((r, c))
if not empty_cells:
return [], {}
filler_words = [w for w in all_words if 2 <= len(w) <= 4 and w not in used_words]
filler_words = sorted(list(set(filler_words)), key=len, reverse=True)
random.shuffle(filler_words)
filled_paths = {}
filled_words = []
remaining_empty = empty_cells.copy()
for word in filler_words:
if len(word) > len(remaining_empty):
continue
for _ in range(500):
if not remaining_empty:
break
start = random.choice(list(remaining_empty))
path = [start]
visited = {start}
ok = True
for _ in range(len(word) - 1):
r, c = path[-1]
candidates = []
for dr, dc in DIRECTIONS:
nr, nc = r + dr, c + dc
if 0 <= nr < grid_size and 0 <= nc < grid_size:
if (nr, nc) in remaining_empty and (nr, nc) not in visited:
candidates.append((nr, nc))
if not candidates:
ok = False
break
nxt = random.choice(candidates)
path.append(nxt)
visited.add(nxt)
if ok and len(path) == len(word):
for (r, c), letter in zip(path, word.upper()):
grid[r][c] = letter
if (r, c) in remaining_empty:
remaining_empty.remove((r, c))
filled_paths[word] = [list(p) for p in path]
filled_words.append(word)
break
for r, c in remaining_empty:
grid[r][c] = random.choice(ALPHABET)
return filled_words, filled_paths
def generate_grid(words, grid_size, all_words, used_words, max_restarts=200):
words_sorted = sorted(words, key=len, reverse=True)
for _ in range(max_restarts):
grid = [['' for _ in range(grid_size)] for _ in range(grid_size)]
occupied = set()
word_paths = {}
success = True
for word in words_sorted:
path = try_place_word(word, grid_size, occupied)
if path is None:
success = False
break
for (r, c), letter in zip(path, word.upper()):
grid[r][c] = letter
occupied.update(path)
word_paths[word] = [list(p) for p in path]
if not success:
continue
all_used = set(words_sorted) | used_words
filled_words, filled_paths = fill_empty_cells(
grid, grid_size, occupied, all_words, all_used
)
word_paths.update(filled_paths)
all_placed_words = words_sorted + filled_words
return grid, word_paths, all_placed_words
return None, None, None
async def save_level(conn, level_num, grid_size, level_name, grid, word_paths, words, translations):
level = await conn.fetchrow(
"""INSERT INTO levels (level_number, grid_size, level, grid_letters, word_paths)
VALUES ($1, $2, $3, $4, $5) RETURNING id""",
level_num, grid_size, level_name, json.dumps(grid), json.dumps(word_paths)
)
level_id = level['id']
for word in words:
trans = translations.get(word, {})
await conn.execute(
"""INSERT INTO level_words (level_id, word_pl, translation_ru, translation_uk, translation_en, level)
VALUES ($1, $2, $3, $4, $5, $6)""",
level_id, word,
trans.get('ru', ''),
trans.get('uk', ''),
trans.get('en', ''),
level_name
)
async def main():
# Конфигурация уровней: 550 уровней на каждый уровень польского
level_configs = [
{
"name": "A1",
"file": "words_a1.csv",
"levels": [
(50, 4, 3), # 50 уровней: 4x4, 3 слова
(100, 5, 4), # 100 уровней: 5x5, 4 слова
(200, 6, 5), # 200 уровней: 6x6, 5 слов
(200, 6, 6), # 200 уровней: 6x6, 6 слов
],
},
{
"name": "A2",
"file": "words_a2.csv",
"levels": [
(100, 6, 5), # 100 уровней: 6x6, 5 слов
(150, 7, 6), # 150 уровней: 7x7, 6 слов
(150, 8, 7), # 150 уровней: 8x8, 7 слов
(150, 8, 8), # 150 уровней: 8x8, 8 слов
],
},
{
"name": "B1",
"file": "words_b1.csv",
"levels": [
(100, 8, 7), # 100 уровней: 8x8, 7 слов
(150, 9, 8), # 150 уровней: 9x9, 8 слов
(150, 10, 9), # 150 уровней: 10x10, 9 слов
(150, 10, 10), # 150 уровней: 10x10, 10 слов
],
},
]
conn = await asyncpg.connect(**DB_CONFIG)
# Очищаем старые данные
await conn.execute("DELETE FROM level_words")
await conn.execute("DELETE FROM user_progress")
await conn.execute("DELETE FROM sentence_progress")
await conn.execute("DELETE FROM word_quiz_progress")
await conn.execute("DELETE FROM bubble_words_progress")
await conn.execute("DELETE FROM levels")
await conn.execute("ALTER SEQUENCE levels_id_seq RESTART WITH 1")
await conn.execute("UPDATE users SET last_completed_level = 0, current_level = 'A1'")
total_generated = 0
for level_config in level_configs:
level_name = level_config["name"]
filename = level_config["file"]
print(f"\n{'='*50}")
print(f"Генерирую уровни для {level_name} из {filename}")
print(f"{'='*50}")
all_words, translations = load_words_from_csv(filename)
print(f"Загружено слов: {len(all_words)}")
if len(all_words) < 10:
print(f"❌ Слишком мало слов в {filename}")
continue
used_words = set()
level_num = 0
for count, grid_size, num_words in level_config["levels"]:
for i in range(count):
level_num += 1
for attempt in range(20):
words = get_words_for_grid(all_words, num_words, grid_size)
if len(words) < num_words:
continue
grid, word_paths, all_placed_words = generate_grid(
words, grid_size, all_words, used_words
)
if grid is not None:
await save_level(
conn, level_num, grid_size, level_name,
grid, word_paths, all_placed_words, translations
)
used_words.update(all_placed_words)
total_generated += 1
print(f"{level_name} Level {level_num}: {grid_size}x{grid_size}, {len(all_placed_words)} words")
break
else:
print(f"{level_name} Level {level_num}: FAILED after 20 attempts")
await conn.close()
print(f"\n🎉 Готово! Сгенерировано уровней: {total_generated}")
if __name__ == "__main__":
asyncio.run(main())
+75
View File
@@ -0,0 +1,75 @@
"""
Заполняет поле options_language в learning_questions.
Определяет, на каком языке показывать варианты ответов:
- 'user' — для вопросов "Что означает...?" (варианты на языке пользователя)
- 'pl' — для остальных (варианты на польском)
Запуск: python3 init_options_language.py
"""
import asyncio
import asyncpg
from config import DB_CONFIG
# Ключевые фразы, которые означают "вопрос на понимание смысла"
USER_LANG_PATTERNS = [
'co oznacza',
'co znaczy',
'что означает',
'що означає',
'what does',
'what means',
'переведи',
'переклади',
'przetłumacz',
'translate',
]
async def main():
pool = await asyncpg.create_pool(**DB_CONFIG)
async with pool.acquire() as conn:
# Получаем все вопросы
questions = await conn.fetch("""
SELECT question_id, prompt_pl, prompt_ru, prompt_uk, prompt_en
FROM learning_questions
""")
user_lang_count = 0
pl_lang_count = 0
for q in questions:
# Собираем все промпты в один текст для проверки
all_prompts = ' '.join([
q['prompt_pl'] or '',
q['prompt_ru'] or '',
q['prompt_uk'] or '',
q['prompt_en'] or '',
]).lower()
# Определяем язык вариантов
is_user_lang = any(pattern in all_prompts for pattern in USER_LANG_PATTERNS)
options_language = 'user' if is_user_lang else 'pl'
await conn.execute("""
UPDATE learning_questions
SET options_language = $2
WHERE question_id = $1
""", q['question_id'], options_language)
if is_user_lang:
user_lang_count += 1
else:
pl_lang_count += 1
print(f"✅ options_language = 'user': {user_lang_count} вопросов")
print(f"✅ options_language = 'pl': {pl_lang_count} вопросов")
print(f" Всего: {user_lang_count + pl_lang_count}")
await pool.close()
print("\n🎉 Готово!")
if __name__ == "__main__":
asyncio.run(main())
+195
View File
@@ -0,0 +1,195 @@
"""
Инициализация данных для чат-учителя:
1. wrong_because_explanations — объяснения ошибок
2. teacher_reactions — банк реакций (Марек)
Запуск: python3 init_teacher_data.py
"""
import asyncio
import asyncpg
from config import DB_CONFIG
# ============ Объяснения ошибок ============
WRONG_BECAUSE_EXPLANATIONS = [
{
"wrong_because": "wrong_word",
"pl": "To nie jest właściwe słowo w tym kontekście.",
"ru": "Это не подходящее слово в данном контексте.",
"uk": "Це не відповідне слово в даному контексті.",
"en": "This is not the right word in this context."
},
{
"wrong_because": "wrong_gender",
"pl": "Uwaga na rodzaj gramatyczny! W języku polskim rzeczowniki mają rodzaje: męski, żeński i nijaki.",
"ru": "Обрати внимание на грамматический род! В польском языке существительные бывают мужского, женского и среднего рода.",
"uk": "Зверни увагу на граматичний рід! У польській мові іменники бувають чоловічого, жіночого та середнього роду.",
"en": "Pay attention to grammatical gender! Polish nouns have masculine, feminine and neuter genders."
},
{
"wrong_because": "wrong_case",
"pl": "Sprawdź przypadek gramatyczny. W języku polskim jest 7 przypadków!",
"ru": "Проверь грамматический падеж. В польском языке 7 падежей!",
"uk": "Перевір граматичний відмінок. У польській мові 7 відмінків!",
"en": "Check the grammatical case. Polish has 7 cases!"
},
{
"wrong_because": "wrong_number",
"pl": "Uwaga na liczbę! Pojedyncza czy mnoga?",
"ru": "Обрати внимание на число! Единственное или множественное?",
"uk": "Зверни увагу на число! Однина чи множина?",
"en": "Pay attention to number! Singular or plural?"
},
{
"wrong_because": "wrong_sentence_type",
"pl": "To nie jest właściwy typ zdania (oznajmujące, pytające, rozkazujące).",
"ru": "Это не правильный тип предложения (повествовательное, вопросительное, повелительное).",
"uk": "Це не правильний тип речення (розповідне, питальне, наказове).",
"en": "This is not the right sentence type (declarative, interrogative, imperative)."
},
{
"wrong_because": "wrong_translation",
"pl": "Tłumaczenie nie jest dokładne. Spróbuj jeszcze raz.",
"ru": "Перевод не точный. Попробуй ещё раз.",
"uk": "Переклад не точний. Спробуй ще раз.",
"en": "The translation is not accurate. Try again."
},
{
"wrong_because": "wrong_context",
"pl": "To słowo nie pasuje do kontekstu zdania.",
"ru": "Это слово не подходит к контексту предложения.",
"uk": "Це слово не підходить до контексту речення.",
"en": "This word doesn't fit the sentence context."
},
]
# ============ Реакции учителя Марека (нейтральные, без гендерной привязки) ============
# 30 на "correct", 20 на "almost", 20 на "wrong"
TEACHER_REACTIONS = [
# ===== ПРАВИЛЬНЫЕ ОТВЕТЫ (30) =====
{"type": "correct", "pl": "Świetnie! Dokładnie tak!", "ru": "Отлично! Именно так!", "uk": "Чудово! Саме так!", "en": "Great! Exactly right!"},
{"type": "correct", "pl": "Brawo! Świetna robota!", "ru": "Браво! Отличная работа!", "uk": "Браво! Чудова робота!", "en": "Bravo! Great job!"},
{"type": "correct", "pl": "Doskonale! Tak trzymaj!", "ru": "Превосходно! Так держать!", "uk": "Чудово! Так тримати!", "en": "Excellent! Keep it up!"},
{"type": "correct", "pl": "Zgadza się! Widzę ogromny postęp!", "ru": "Верно! Вижу огромный прогресс!", "uk": "Вірно! Бачу величезний прогрес!", "en": "Correct! I see huge progress!"},
{"type": "correct", "pl": "Perfekcyjnie! Zasługujesz na pochwałę!", "ru": "Идеально! Это заслуживает похвалы!", "uk": "Ідеально! Це заслуговує на похвалу!", "en": "Perfect! This deserves praise!"},
{"type": "correct", "pl": "Tak jest! Bez żadnych błędów!", "ru": "Так точно! Без единой ошибки!", "uk": "Так точно! Без жодної помилки!", "en": "That's it! No mistakes at all!"},
{"type": "correct", "pl": "Pięknie! Od razu widać postępy!", "ru": "Красиво! Сразу виден прогресс!", "uk": "Гарно! Одразу видно прогрес!", "en": "Beautiful! Progress is visible!"},
{"type": "correct", "pl": "O tak! To jest poprawna odpowiedź!", "ru": "О да! Это правильный ответ!", "uk": "О так! Це правильна відповідь!", "en": "Oh yes! That's the correct answer!"},
{"type": "correct", "pl": "Wspaniale! Z każdym razem lepiej!", "ru": "Замечательно! С каждым разом лучше!", "uk": "Чудово! З кожним разом краще!", "en": "Wonderful! Better every time!"},
{"type": "correct", "pl": "Dokładnie o to chodziło!", "ru": "Именно это и имелось в виду!", "uk": "Саме це і малося на увазі!", "en": "That's exactly what I meant!"},
{"type": "correct", "pl": "Super! To naprawdę imponujące!", "ru": "Супер! Это впечатляет!", "uk": "Супер! Це вражає!", "en": "Super! That's impressive!"},
{"type": "correct", "pl": "Zgadza się! Polskie słowa nie są ci straszne!", "ru": "Верно! Польские слова не страшны!", "uk": "Вірно! Польські слова не страшні!", "en": "Right! Polish words don't scare you!"},
{"type": "correct", "pl": "Idealnie! Jak rodowity Polak!", "ru": "Идеально! Как у носителя языка!", "uk": "Ідеально! Як у носія мови!", "en": "Perfect! Like a native speaker!"},
{"type": "correct", "pl": "Masz rację! Pamięć działa świetnie!", "ru": "Верно! Память работает отлично!", "uk": "Вірно! Пам'ять працює чудово!", "en": "Right! Memory works great!"},
{"type": "correct", "pl": "Świetna odpowiedź! Tak trzymaj dalej!", "ru": "Отличный ответ! Продолжай в том же духе!", "uk": "Чудова відповідь! Продовжуй у тому ж дусі!", "en": "Great answer! Keep it up!"},
{"type": "correct", "pl": "Brawo! Widzę, że naprawdę się uczysz!", "ru": "Браво! Видно, что идёт настоящая учёба!", "uk": "Браво! Видно, що йде справжнє навчання!", "en": "Bravo! Real learning is happening!"},
{"type": "correct", "pl": "Oczywiście! Dokładnie tak!", "ru": "Конечно! Именно так!", "uk": "Звісно! Саме так!", "en": "Of course! Exactly like that!"},
{"type": "correct", "pl": "Znakomicie! Nie mogę się doczekać następnego pytania!", "ru": "Великолепно! Жду следующего вопроса!", "uk": "Чудово! Чекаю наступного питання!", "en": "Excellent! Can't wait for the next question!"},
{"type": "correct", "pl": "Tak! To poprawna forma!", "ru": "Да! Это правильная форма!", "uk": "Так! Це правильна форма!", "en": "Yes! That's the correct form!"},
{"type": "correct", "pl": "Prawidłowo! Jestem dumny z twoich postępów!", "ru": "Правильно! Я горжусь твоими успехами!", "uk": "Правильно! Я пишаюся твоїми успіхами!", "en": "Correct! I'm proud of your progress!"},
{"type": "correct", "pl": "Dobrze! Nawet nie musiałeś się zastanawiać!", "ru": "Хорошо! Даже не пришлось задумываться!", "uk": "Добре! Навіть не довелося задумуватися!", "en": "Good! Didn't even hesitate!"},
{"type": "correct", "pl": "Dokładnie! Język polski nie ma przed tobą tajemnic!", "ru": "Точно! У польского языка нет секретов!", "uk": "Точно! У польської мови немає секретів!", "en": "Exactly! Polish has no secrets!"},
{"type": "correct", "pl": "Tak jest! Ciężka praca się opłaca!", "ru": "Так! Усердная работа окупается!", "uk": "Так! Наполеглива праця окупається!", "en": "That's it! Hard work pays off!"},
{"type": "correct", "pl": "Doskonale! Zasługujesz na przerwę, ale najpierw kolejne pytanie!", "ru": "Отлично! Заслужил(а) перерыв, но сначала следующий вопрос!", "uk": "Чудово! Заслужив(ла) перерву, але спочатку наступне питання!", "en": "Excellent! You deserve a break, but first the next question!"},
{"type": "correct", "pl": "Fantastycznie! Twoja wiedza rośnie!", "ru": "Фантастика! Знания растут!", "uk": "Фантастика! Знання ростуть!", "en": "Fantastic! Knowledge is growing!"},
{"type": "correct", "pl": "Świetny wynik! Jestem pod wrażeniem!", "ru": "Отличный результат! Я впечатлён!", "uk": "Чудовий результат! Я вражений!", "en": "Great result! I'm impressed!"},
{"type": "correct", "pl": "Brawo! Tak trzymaj!", "ru": "Браво! Так держать!", "uk": "Браво! Так тримати!", "en": "Bravo! Keep it up!"},
{"type": "correct", "pl": "Zgadza się! Coraz lepiej ci idzie!", "ru": "Верно! Всё лучше и лучше получается!", "uk": "Вірно! Все краще і краще виходить!", "en": "Right! Getting better and better!"},
{"type": "correct", "pl": "Dokładnie! Nic dodać, nic ująć!", "ru": "Точно! Ни добавить, ни убавить!", "uk": "Точно! Ні додати, ні відняти!", "en": "Exactly! Nothing to add, nothing to subtract!"},
{"type": "correct", "pl": "Wspaniała robota! Jestem naprawdę dumny!", "ru": "Отличная работа! Я правда горжусь!", "uk": "Чудова робота! Я справді пишаюся!", "en": "Wonderful work! Truly proud!"},
# ===== ПОЧТИ ПРАВИЛЬНО (20) =====
{"type": "almost", "pl": "Prawie! Jeszcze chwila...", "ru": "Почти! Ещё чуть-чуть...", "uk": "Майже! Ще трохи...", "en": "Almost! Just a little more..."},
{"type": "almost", "pl": "Blisko! Spróbuj jeszcze raz.", "ru": "Близко! Попробуй ещё раз.", "uk": "Близько! Спробуй ще раз.", "en": "Close! Try again."},
{"type": "almost", "pl": "Niewiele brakowało!", "ru": "Чуть-чуть не хватило!", "uk": "Трішки не вистачило!", "en": "So close!"},
{"type": "almost", "pl": "Jesteś na dobrej drodze!", "ru": "Ты на правильном пути!", "uk": "Ти на правильному шляху!", "en": "You're on the right track!"},
{"type": "almost", "pl": "Prawie dobrze! Jeszcze raz!", "ru": "Почти хорошо! Ещё раз!", "uk": "Майже добре! Ще раз!", "en": "Almost right! One more time!"},
{"type": "almost", "pl": "Jeszcze troszeczkę...", "ru": "Ещё чуточку...", "uk": "Ще трішечки...", "en": "Just a tiny bit more..."},
{"type": "almost", "pl": "Blisko, ale nie dokładnie. Pomyśl jeszcze chwilę.", "ru": "Близко, но не точно. Подумай ещё немного.", "uk": "Близько, але не точно. Подумай ще трохи.", "en": "Close, but not quite. Think a bit more."},
{"type": "almost", "pl": "Dobra intuicja! Ale spróbuj poprawić.", "ru": "Хорошая интуиция! Но попробуй исправить.", "uk": "Гарна інтуїція! Але спробуй виправити.", "en": "Good intuition! But try to correct it."},
{"type": "almost", "pl": "To nie jest złe, ale nie do końca to, o co pytam.", "ru": "Это неплохо, но не совсем то, о чём я спрашиваю.", "uk": "Це непогано, але не зовсім те, про що я питаю.", "en": "Not bad, but not quite what I'm asking."},
{"type": "almost", "pl": "Jesteś bardzo blisko! Mała poprawka i będzie idealnie.", "ru": "Очень близко! Маленькая поправка и будет идеально.", "uk": "Дуже близько! Маленьке виправлення і буде ідеально.", "en": "Very close! Small correction and it'll be perfect."},
{"type": "almost", "pl": "Prawie! Sprawdź jeszcze raz pisownię.", "ru": "Почти! Проверь ещё раз написание.", "uk": "Майже! Перевір ще раз написання.", "en": "Almost! Check the spelling again."},
{"type": "almost", "pl": "Niewielka pomyłka! Skup się na szczegółach.", "ru": "Небольшая ошибка! Сосредоточься на деталях.", "uk": "Невелика помилка! Зосередься на деталях.", "en": "Small mistake! Focus on the details."},
{"type": "almost", "pl": "Blisko! Jeden mały krok do sukcesu.", "ru": "Близко! Один маленький шаг до успеха.", "uk": "Близько! Один маленький крок до успіху.", "en": "Close! One small step to success."},
{"type": "almost", "pl": "Jesteś blisko! Ale nie tym razem.", "ru": "Близко! Но не в этот раз.", "uk": "Близько! Але не цього разу.", "en": "Close! But not this time."},
{"type": "almost", "pl": "Prawie! Nie zatrzymuj się teraz.", "ru": "Почти! Не останавливайся сейчас.", "uk": "Майже! Не зупиняйся зараз.", "en": "Almost! Don't stop now."},
{"type": "almost", "pl": "Jeszcze jeden krok! Spróbuj ponownie.", "ru": "Ещё один шаг! Попробуй снова.", "uk": "Ще один крок! Спробуй знову.", "en": "One more step! Try again."},
{"type": "almost", "pl": "Niewiele brakuje do perfekcji!", "ru": "Немного не хватает до совершенства!", "uk": "Трішки не вистачає до досконалості!", "en": "Just a bit short of perfection!"},
{"type": "almost", "pl": "Bardzo blisko! Jeszcze jedna próba.", "ru": "Очень близко! Ещё одна попытка.", "uk": "Дуже близько! Ще одна спроба.", "en": "Very close! One more attempt."},
{"type": "almost", "pl": "Prawie udało się! Nie poddawaj się.", "ru": "Почти получилось! Не сдавайся.", "uk": "Майже вийшло! Не здавайся.", "en": "Almost got it! Don't give up."},
{"type": "almost", "pl": "Mała poprawka i będzie idealnie!", "ru": "Маленькая поправка и будет идеально!", "uk": "Маленьке виправлення і буде ідеально!", "en": "Small correction and it'll be perfect!"},
# ===== НЕПРАВИЛЬНЫЕ ОТВЕТЫ (20) =====
{"type": "wrong", "pl": "Nie tym razem. Nie poddawaj się!", "ru": "Не в этот раз. Не сдавайся!", "uk": "Не цього разу. Не здавайся!", "en": "Not this time. Don't give up!"},
{"type": "wrong", "pl": "To nie to. Spróbuj jeszcze raz!", "ru": "Это не то. Попробуй ещё раз!", "uk": "Це не те. Спробуй ще раз!", "en": "That's not it. Try again!"},
{"type": "wrong", "pl": "Zdarza się! Ważne, że próbujesz.", "ru": "Бывает! Главное, что ты пытаешься.", "uk": "Буває! Головне, що ти намагаєшся.", "en": "It happens! What matters is that you try."},
{"type": "wrong", "pl": "Błąd to część nauki!", "ru": "Ошибка — часть обучения!", "uk": "Помилка — частина навчання!", "en": "Mistakes are part of learning!"},
{"type": "wrong", "pl": "Nie szkodzi! Uczymy się na błędach.", "ru": "Ничего страшного! Мы учимся на ошибках.", "uk": "Нічого страшного! Ми вчимося на помилках.", "en": "No worries! We learn from mistakes."},
{"type": "wrong", "pl": "Nie martw się! Każdy mistrz kiedyś zaczynał od zera.", "ru": "Не переживай! Каждый мастер когда-то начинал с нуля.", "uk": "Не хвилюйся! Кожен майстер колись починав з нуля.", "en": "Don't worry! Every master started from zero."},
{"type": "wrong", "pl": "Pomyłka to dobry znak — znaczy, że próbujesz!", "ru": "Ошибка — хороший знак, значит есть попытка!", "uk": "Помилка — гарний знак, значить є спроба!", "en": "A mistake is a good sign — it means you're trying!"},
{"type": "wrong", "pl": "Nie udało się, ale nie przestawaj!", "ru": "Не получилось, но не останавливайся!", "uk": "Не вийшло, але не зупиняйся!", "en": "Didn't work, but don't stop!"},
{"type": "wrong", "pl": "Sprawdź dokładnie i spróbuj ponownie.", "ru": "Проверь внимательно и попробуй снова.", "uk": "Перевір уважно і спробуй знову.", "en": "Check carefully and try again."},
{"type": "wrong", "pl": "Nie tym razem, ale doceniam twoje zaangażowanie!", "ru": "Не в этот раз, но я ценю усердие!", "uk": "Не цього разу, але я ціную старанність!", "en": "Not this time, but I appreciate the effort!"},
{"type": "wrong", "pl": "Spokojnie! Nawet najlepsi popełniają błędy.", "ru": "Спокойно! Даже лучшие ошибаются.", "uk": "Спокійно! Навіть найкращі помиляються.", "en": "Relax! Even the best make mistakes."},
{"type": "wrong", "pl": "Nie poddawaj się! Jesteś bliżej niż myślisz.", "ru": "Не сдавайся! Ты ближе, чем думаешь.", "uk": "Не здавайся! Ти ближче, ніж думаєш.", "en": "Don't give up! You're closer than you think."},
{"type": "wrong", "pl": "Spróbuj inaczej! Może pomoże inne podejście.", "ru": "Попробуй иначе! Может, поможет другой подход.", "uk": "Спробуй інакше! Може, допоможе інший підхід.", "en": "Try differently! Maybe another approach will help."},
{"type": "wrong", "pl": "Nie zniechęcaj się! To normalny etap nauki.", "ru": "Не расстраивайся! Это нормальный этап обучения.", "uk": "Не засмучуйся! Це нормальний етап навчання.", "en": "Don't be discouraged! It's a normal stage of learning."},
{"type": "wrong", "pl": "Każdy błąd to lekcja na przyszłość!", "ru": "Каждая ошибка — урок на будущее!", "uk": "Кожна помилка — урок на майбутнє!", "en": "Every mistake is a lesson for the future!"},
{"type": "wrong", "pl": "Nie martw się! Mózg najlepiej uczy się na błędach.", "ru": "Не переживай! Мозг лучше всего учится на ошибках.", "uk": "Не хвилюйся! Мозок найкраще вчиться на помилках.", "en": "Don't worry! The brain learns best from mistakes."},
{"type": "wrong", "pl": "To normalne! Nawet ja czasem się mylę.", "ru": "Это нормально! Даже я иногда ошибаюсь.", "uk": "Це нормально! Навіть я іноді помиляюся.", "en": "That's normal! Even I make mistakes sometimes."},
{"type": "wrong", "pl": "Ważne, że się uczysz! Błędy to część drogi.", "ru": "Важно, что ты учишься! Ошибки — часть пути.", "uk": "Важливо, що ти вчишся! Помилки — частина шляху.", "en": "What matters is that you're learning! Mistakes are part of the journey."},
{"type": "wrong", "pl": "Nie przejmuj się! Następnym razem się uda.", "ru": "Не переживай! В следующий раз получится.", "uk": "Не хвилюйся! Наступного разу вийде.", "en": "Don't worry! Next time it'll work."},
{"type": "wrong", "pl": "Uczymy się razem! To dopiero początek.", "ru": "Мы учимся вместе! Это только начало.", "uk": "Ми вчимося разом! Це тільки початок.", "en": "We're learning together! This is just the beginning."},
]
async def main():
pool = await asyncpg.create_pool(**DB_CONFIG)
async with pool.acquire() as conn:
# ============ Очищаем старые реакции ============
await conn.execute("DELETE FROM teacher_reactions")
print("🗑️ Старые реакции удалены")
# ============ Заполняем wrong_because_explanations ============
for item in WRONG_BECAUSE_EXPLANATIONS:
await conn.execute("""
INSERT INTO wrong_because_explanations (wrong_because, explanation_pl, explanation_ru, explanation_uk, explanation_en)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (wrong_because) DO UPDATE SET
explanation_pl = $2,
explanation_ru = $3,
explanation_uk = $4,
explanation_en = $5
""", item["wrong_because"], item["pl"], item["ru"], item["uk"], item["en"])
print(f"✅ Добавлено {len(WRONG_BECAUSE_EXPLANATIONS)} объяснений ошибок")
# ============ Заполняем teacher_reactions ============
correct_count = 0
almost_count = 0
wrong_count = 0
for item in TEACHER_REACTIONS:
await conn.execute("""
INSERT INTO teacher_reactions (reaction_type, text_pl, text_ru, text_uk, text_en)
VALUES ($1, $2, $3, $4, $5)
""", item["type"], item["pl"], item["ru"], item["uk"], item["en"])
if item["type"] == "correct":
correct_count += 1
elif item["type"] == "almost":
almost_count += 1
else:
wrong_count += 1
print(f"✅ Добавлено реакций: correct={correct_count}, almost={almost_count}, wrong={wrong_count}")
print(f" Всего: {len(TEACHER_REACTIONS)}")
await pool.close()
print("\n🎉 Готово! Марек готов к работе!")
if __name__ == "__main__":
asyncio.run(main())
+55
View File
@@ -0,0 +1,55 @@
import csv
import asyncpg
import asyncio
from config import DB_CONFIG
async def load_all():
pool = await asyncpg.create_pool(**DB_CONFIG)
async with pool.acquire() as conn:
level_files = [
("A1", "words_a1.csv"),
("A2", "words_a2.csv"),
("B1", "words_b1.csv"),
]
total_added = 0
for level, fname in level_files:
print(f'\n{"="*50}')
print(f'Loading {fname} (level {level})...')
print(f'{"="*50}')
# 🔧 ФИКС: удаляем ВСЕ строки уровня, независимо от level_id
deleted = await conn.execute(
"DELETE FROM level_words WHERE level = $1",
level
)
print(f' Deleted old entries: {deleted}')
count = 0
with open(fname, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
word = row['polish'].strip().lower()
if word:
await conn.execute(
'''INSERT INTO level_words (level_id, word_pl, translation_ru, translation_uk, translation_en, level)
VALUES (NULL, $1, $2, $3, $4, $5)
ON CONFLICT (level, word_pl) DO NOTHING''',
word,
row.get('russian', ''),
row.get('ukrainian', ''),
row.get('english', ''),
level
)
count += 1
total_added += count
print(f' Added {count} words from {fname} (level {level})')
await pool.close()
print(f'\n🎉 Готово! Всего загружено {total_added} слов.')
if __name__ == '__main__':
asyncio.run(load_all())
+286
View File
@@ -0,0 +1,286 @@
import asyncio
import logging
import os
import re
import time
from collections import defaultdict
from pathlib import Path
from fastapi import FastAPI, Request, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from fastapi.staticfiles import StaticFiles
from config import FRONTEND_ORIGIN, UPLOADS_DIR
from database import init_db
from routers import users, game, hints, leaderboard, sentences, achievements, wordquiz, bubblewords, pronunciation, assistant, learning, tts, typing, teacher, support, admin, feed, admin_content
from routers.sentences import init_sentences
from routers.achievements import init_achievements
from routers.learning_data import init_learning_data
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# ============ Версия ============
# Читается из .env (переменная APP_VERSION). Если не задана — fallback "1.0.0".
# Фронт живёт на Cloudflare Pages, там своя версия в index.html — они не обязаны совпадать.
# При деплое новой версии фронта обнови APP_VERSION в .env и перезапусти filwords-api.
APP_VERSION = os.getenv("APP_VERSION", "1.0.0")
# ============ Sentry (мониторинг ошибок) ============
# ВАЖНО: инициализация ДО создания FastAPI-приложения
SENTRY_DSN = os.getenv("SENTRY_DSN", "")
if SENTRY_DSN:
try:
import sentry_sdk
from sentry_sdk.integrations.fastapi import FastApiIntegration
from sentry_sdk.integrations.starlette import StarletteIntegration
sentry_sdk.init(
dsn=SENTRY_DSN,
integrations=[
StarletteIntegration(transaction_style="endpoint"),
FastApiIntegration(transaction_style="endpoint"),
],
traces_sample_rate=0.2, # 20% запросов для performance monitoring
environment=os.getenv("ENV", "production"),
release=f"filwords@{APP_VERSION}",
send_default_pii=False, # НЕ отправляем IP/headers/cookies пользователей
)
logger.info(f"Sentry initialized (release=filwords@{APP_VERSION})")
except Exception as e:
logger.error(f"Failed to initialize Sentry: {e}")
else:
logger.warning("SENTRY_DSN not set — Sentry disabled")
app = FastAPI(title="Filwords PL API")
# ============ Rate Limiting ============
class RateLimiter:
def __init__(self):
self.requests = defaultdict(list)
self.limits = {
"default": {"max_requests": 60, "window": 60},
"/game/check-word": {"max_requests": 30, "window": 60},
"/game/complete-level": {"max_requests": 10, "window": 60},
"/sentences/check": {"max_requests": 20, "window": 60},
"/leaderboard": {"max_requests": 10, "window": 60},
"/assistant/chat": {"max_requests": 20, "window": 60},
"/learning/answer": {"max_requests": 30, "window": 60},
"/tts": {"max_requests": 30, "window": 60},
"/review": {"max_requests": 10, "window": 60},
"/typing/check": {"max_requests": 20, "window": 60},
"/teacher/answer": {"max_requests": 30, "window": 60},
"/teacher/start": {"max_requests": 10, "window": 60},
"/teacher/explain": {"max_requests": 10, "window": 60},
"/teacher/translate": {"max_requests": 10, "window": 60},
"/teacher/example": {"max_requests": 10, "window": 60},
"/support/send": {"max_requests": 10, "window": 60},
"/support/admin-reply": {"max_requests": 30, "window": 60},
"/support/translate": {"max_requests": 20, "window": 60},
# 🔧 НОВОЕ (18.09.2026): лимиты для feed
"/admin/feed/post": {"max_requests": 20, "window": 60},
"/admin/feed/upload-image": {"max_requests": 10, "window": 60},
"/admin/feed/translate": {"max_requests": 10, "window": 60},
}
self._cleanup_task = None
def _cleanup(self):
now = time.time()
for key in list(self.requests.keys()):
self.requests[key] = [t for t in self.requests[key] if now - t < 60]
if not self.requests[key]:
del self.requests[key]
async def start_cleanup(self):
while True:
await asyncio.sleep(60)
self._cleanup()
def is_allowed(self, key: str, path: str) -> bool:
now = time.time()
limit_config = self.limits.get(path, self.limits["default"])
max_requests = limit_config["max_requests"]
window = limit_config["window"]
self.requests[key] = [t for t in self.requests[key] if now - t < window]
if len(self.requests[key]) >= max_requests:
return False
self.requests[key].append(now)
return True
def get_retry_after(self, key: str, path: str) -> int:
if not self.requests[key]:
return 0
limit_config = self.limits.get(path, self.limits["default"])
window = limit_config["window"]
oldest = min(self.requests[key])
return max(0, int(window - (time.time() - oldest)))
rate_limiter = RateLimiter()
# ============ Rate Limiting Middleware ============
@app.middleware("http")
async def rate_limit_middleware(request: Request, call_next):
path = request.url.path
if path in ["/", "/health"]:
return await call_next(request)
client_ip = request.client.host if request.client else "unknown"
rate_key = f"{client_ip}:{path}"
if not rate_limiter.is_allowed(rate_key, path):
retry_after = rate_limiter.get_retry_after(rate_key, path)
return JSONResponse(
status_code=429,
content={
"error": "Too many requests",
"retry_after": retry_after
},
headers={"Retry-After": str(retry_after)}
)
start_time = time.time()
response = await call_next(request)
duration = time.time() - start_time
if duration > 1.0:
logger.warning(f"Slow request: {request.method} {path} - {duration:.2f}s")
return response
# ============ CORS ============
# 🔧 ОБНОВЛЕНО: добавлен PUT — нужен для редактирования feed-постов
app.add_middleware(
CORSMiddleware,
allow_origins=[FRONTEND_ORIGIN],
allow_credentials=False,
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allow_headers=["*"],
)
# ============ 🔧 НОВОЕ (18.09.2026): статика для картинок feed-постов ============
# Файлы лежат в /root/filwordspl/backend/uploads/feed/{filename}
# URL на фронте: https://api.filwordspl.com/uploads/feed/{filename}
app.mount("/uploads", StaticFiles(directory=UPLOADS_DIR), name="uploads")
# ============ Роутеры ============
app.include_router(users.router)
app.include_router(game.router)
app.include_router(hints.router)
app.include_router(leaderboard.router)
app.include_router(sentences.router)
app.include_router(achievements.router)
app.include_router(wordquiz.router)
app.include_router(bubblewords.router)
app.include_router(pronunciation.router)
app.include_router(assistant.router)
app.include_router(learning.router)
app.include_router(tts.router)
app.include_router(typing.router)
app.include_router(teacher.router)
app.include_router(support.router)
app.include_router(admin.router)
# 🔧 НОВОЕ (18.09.2026): feed — посты карусели (публичный + админский)
app.include_router(feed.router)
# 🔧 НОВОЕ (19.09.2026): admin content — контент-менеджер (только для админов)
app.include_router(admin_content.router)
# ============ Фоновое обновление кэша рейтинга ============
async def refresh_leaderboard_cache_loop():
await asyncio.sleep(10)
while True:
try:
from database import get_pool
pool = await get_pool()
async with pool.acquire() as conn:
await conn.execute("REFRESH MATERIALIZED VIEW CONCURRENTLY leaderboard_cache")
except Exception as e:
logger.error(f"Failed to refresh leaderboard cache: {e}")
await asyncio.sleep(30)
# ============ Startup ============
@app.on_event("startup")
async def startup():
await init_db()
await init_sentences()
await init_achievements()
await init_learning_data()
asyncio.create_task(rate_limiter.start_cleanup())
from database import get_pool
pool = await get_pool()
async with pool.acquire() as conn:
# Пересоздаём materialized view с новой структурой (с level)
await conn.execute("DROP MATERIALIZED VIEW IF EXISTS leaderboard_cache")
await conn.execute("""
CREATE MATERIALIZED VIEW IF NOT EXISTS leaderboard_cache AS
SELECT
u.telegram_id,
u.username,
u.current_level,
COALESCE(ws.total, 0) + COALESCE(s.total, 0) + COALESCE(wq.total, 0) + COALESCE(bw.total, 0) as total_score,
COALESCE(ws.total, 0) as wordsearch_score,
COALESCE(s.total, 0) as sentence_score,
COALESCE(wq.total, 0) as wordquiz_score,
COALESCE(wq.words_guessed, 0) as words_guessed,
COALESCE(bw.total, 0) as bubblewords_score,
COALESCE(bw.levels_completed, 0) as bubble_levels_completed
FROM users u
LEFT JOIN (
SELECT user_id, SUM(score) as total
FROM user_progress
WHERE completed_at IS NOT NULL
GROUP BY user_id
) ws ON u.telegram_id = ws.user_id
LEFT JOIN (
SELECT user_id, SUM(score) as total
FROM sentence_progress
WHERE completed_at IS NOT NULL
GROUP BY user_id
) s ON u.telegram_id = s.user_id
LEFT JOIN (
SELECT user_id, total_score as total, words_guessed
FROM word_quiz_progress
) wq ON u.telegram_id = wq.user_id
LEFT JOIN (
SELECT user_id, total_score as total, levels_completed
FROM bubble_words_progress
) bw ON u.telegram_id = bw.user_id
WHERE COALESCE(ws.total, 0) + COALESCE(s.total, 0) + COALESCE(wq.total, 0) + COALESCE(bw.total, 0) > 0
ORDER BY total_score DESC
""")
await conn.execute("""
CREATE UNIQUE INDEX IF NOT EXISTS idx_leaderboard_cache_user
ON leaderboard_cache (telegram_id)
""")
logger.info("Leaderboard cache created")
asyncio.create_task(refresh_leaderboard_cache_loop())
logger.info(f"Filwords API v{APP_VERSION} started")
# ============ Базовые эндпоинты ============
@app.get("/")
async def root():
return {"status": "ok", "app": "Filwords PL", "version": APP_VERSION}
@app.get("/health")
async def health():
try:
from database import get_pool
pool = await get_pool()
async with pool.acquire() as conn:
await conn.fetchval("SELECT 1")
return {"status": "ok", "database": "connected"}
except Exception as e:
logger.error(f"Healthcheck failed: {e}")
return JSONResponse(
status_code=503,
content={"status": "error", "database": "disconnected"}
)
+17
View File
@@ -0,0 +1,17 @@
[pytest]
asyncio_mode = auto
asyncio_default_fixture_loop_scope = session
asyncio_default_test_loop_scope = session
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
pythonpath = .
addopts =
-v
--tb=short
--strict-markers
markers =
asyncio: async test
integration: tests requiring real DB connection
unit: pure logic tests (no DB)
+38
View File
@@ -0,0 +1,38 @@
aiohappyeyeballs==2.7.1
aiohttp==3.14.3
aiosignal==1.4.0
annotated-doc==0.0.4
annotated-types==0.8.0
anyio==4.14.2
asyncpg==0.31.0
attrs==26.1.0
certifi==2026.7.22
click==8.4.2
edge-tts==7.2.8
fastapi==0.140.0
frozenlist==1.8.0
h11==0.16.0
httpcore==1.0.9
httpx==0.28.1
idna==3.18
iniconfig==2.3.0
multidict==6.7.1
packaging==26.3
pillow==12.3.0
pluggy==1.6.0
propcache==0.5.2
pydantic==2.13.4
pydantic_core==2.46.4
Pygments==2.21.0
pytest==9.1.1
pytest-asyncio==1.4.0
python-dotenv==1.2.2
python-multipart==0.0.32
sentry-sdk==2.69.1
starlette==1.3.1
tabulate==0.10.0
typing-inspection==0.4.2
typing_extensions==4.16.0
urllib3==2.7.0
uvicorn==0.51.0
yarl==1.24.5
+65
View File
@@ -0,0 +1,65 @@
"""
Сброс прогресса пользователя по разделу "Учитель" и "Уроки".
Очищает: user_learning_answers, user_lesson_progress, user_topic_progress,
user_skill_mastery, learning_sessions, teacher_chat_history, teacher_ai_usage.
Запуск: python3 reset_my_progress.py
"""
import asyncio
import asyncpg
from config import DB_CONFIG
# ID пользователя для сброса (твой Telegram ID)
USER_ID = 6694989403
async def main():
pool = await asyncpg.create_pool(**DB_CONFIG)
async with pool.acquire() as conn:
# Проверяем, что пользователь существует
user_exists = await conn.fetchval("""
SELECT 1 FROM users WHERE telegram_id = $1
""", USER_ID)
if not user_exists:
print(f"❌ Пользователь {USER_ID} не найден!")
await pool.close()
return
# Список таблиц для очистки
tables = [
"user_learning_answers",
"user_lesson_progress",
"user_topic_progress",
"user_skill_mastery",
"learning_sessions",
"teacher_chat_history",
"teacher_ai_usage",
]
for table in tables:
result = await conn.execute(f"""
DELETE FROM {table} WHERE user_id = $1
""", USER_ID)
affected = int(result.split()[-1])
print(f"🗑️ {table}: удалено {affected} записей")
# Также очищаем ai_user_context
result = await conn.execute("""
DELETE FROM ai_user_context WHERE user_id = $1
""", USER_ID)
print(f"🗑️ ai_user_context: удалено {result.split()[-1]} записей")
# Сбрасываем last_completed_level
await conn.execute("""
UPDATE users SET last_completed_level = 0 WHERE telegram_id = $1
""", USER_ID)
print("✅ last_completed_level сброшен на 0")
await pool.close()
print("\n🎉 Готово! Можно начинать заново!")
if __name__ == "__main__":
asyncio.run(main())
View File
+530
View File
@@ -0,0 +1,530 @@
from fastapi import APIRouter, Request, HTTPException
from auth import require_auth
from database import get_pool
router = APIRouter(prefix="/achievements", tags=["achievements"])
# ============ Данные 52 достижений ============
ACHIEVEMENTS_DATA = [
# ===== Word Search (11) =====
{"code": "first_level", "category": "wordsearch", "icon": "🔰",
"name_pl": "Pierwszy krok", "name_ru": "Первый шаг", "name_uk": "Перший крок", "name_en": "First Step",
"desc_pl": "Ukończ 1 poziom filwordów", "desc_ru": "Пройти 1 уровень филвордов", "desc_uk": "Пройти 1 рівень філвордів", "desc_en": "Complete 1 word search level",
"type": "levels_completed", "value": 1},
{"code": "ten_levels", "category": "wordsearch", "icon": "",
"name_pl": "Gracz", "name_ru": "Игрок", "name_uk": "Гравець", "name_en": "Player",
"desc_pl": "Ukończ 10 poziomów filwordów", "desc_ru": "Пройти 10 уровней филвордов", "desc_uk": "Пройти 10 рівнів філвордів", "desc_en": "Complete 10 word search levels",
"type": "levels_completed", "value": 10},
{"code": "fifty_levels", "category": "wordsearch", "icon": "🏅",
"name_pl": "Ekspert", "name_ru": "Эксперт", "name_uk": "Експерт", "name_en": "Expert",
"desc_pl": "Ukończ 50 poziomów filwordów", "desc_ru": "Пройти 50 уровней филвордов", "desc_uk": "Пройти 50 рівнів філвордів", "desc_en": "Complete 50 word search levels",
"type": "levels_completed", "value": 50},
{"code": "hundred_levels", "category": "wordsearch", "icon": "👑",
"name_pl": "Król", "name_ru": "Король", "name_uk": "Король", "name_en": "King",
"desc_pl": "Ukończ 100 poziomów filwordów", "desc_ru": "Пройти 100 уровней филвордов", "desc_uk": "Пройти 100 рівнів філвордів", "desc_en": "Complete 100 word search levels",
"type": "levels_completed", "value": 100},
{"code": "ws_200", "category": "wordsearch", "icon": "🎯",
"name_pl": "Mistrz", "name_ru": "Мастер", "name_uk": "Майстер", "name_en": "Master",
"desc_pl": "Ukończ 200 poziomów filwordów", "desc_ru": "Пройти 200 уровней филвордов", "desc_uk": "Пройти 200 рівнів філвордів", "desc_en": "Complete 200 word search levels",
"type": "levels_completed", "value": 200},
{"code": "ws_500", "category": "wordsearch", "icon": "🌟",
"name_pl": "Legenda filwordów", "name_ru": "Легенда филвордов", "name_uk": "Легенда філвордів", "name_en": "Word Search Legend",
"desc_pl": "Ukończ 500 poziomów filwordów", "desc_ru": "Пройти 500 уровней филвордов", "desc_uk": "Пройти 500 рівнів філвордів", "desc_en": "Complete 500 word search levels",
"type": "levels_completed", "value": 500},
{"code": "no_hints", "category": "wordsearch", "icon": "💡",
"name_pl": "Bystrzak", "name_ru": "Умница", "name_uk": "Розумник", "name_en": "Smart",
"desc_pl": "Ukończ poziom bez podpowiedzi", "desc_ru": "Пройти уровень без подсказок", "desc_uk": "Пройти рівень без підказок", "desc_en": "Complete a level without hints",
"type": "no_hints_level", "value": 1},
{"code": "streak_5", "category": "wordsearch", "icon": "🔥",
"name_pl": "Seria", "name_ru": "Серия", "name_uk": "Серія", "name_en": "Streak",
"desc_pl": "Ukończ 5 poziomów z rzędu", "desc_ru": "Пройти 5 уровней подряд", "desc_uk": "Пройти 5 рівнів поспіль", "desc_en": "Complete 5 levels in a row",
"type": "level_streak", "value": 5},
{"code": "speed_60", "category": "wordsearch", "icon": "",
"name_pl": "Szybki", "name_ru": "Быстрый", "name_uk": "Швидкий", "name_en": "Fast",
"desc_pl": "Ukończ poziom w mniej niż 60 sekund", "desc_ru": "Пройти уровень быстрее 60 секунд", "desc_uk": "Пройти рівень швидше 60 секунд", "desc_en": "Complete a level in under 60 seconds",
"type": "speed_level", "value": 60},
{"code": "ws_speed_30", "category": "wordsearch", "icon": "🚀",
"name_pl": "Błyskawica", "name_ru": "Молния", "name_uk": "Блискавка", "name_en": "Lightning",
"desc_pl": "Ukończ poziom w mniej niż 30 sekund", "desc_ru": "Пройти уровень быстрее 30 секунд", "desc_uk": "Пройти рівень швидше 30 секунд", "desc_en": "Complete a level in under 30 seconds",
"type": "speed_level", "value": 30},
{"code": "perfect_5", "category": "wordsearch", "icon": "🎯",
"name_pl": "Snajper", "name_ru": "Снайпер", "name_uk": "Снайпер", "name_en": "Sniper",
"desc_pl": "Znajdź 5 słów bez błędu", "desc_ru": "Найти 5 слов подряд без ошибок", "desc_uk": "Знайти 5 слів без помилки", "desc_en": "Find 5 words without mistakes",
"type": "perfect_words", "value": 5},
# ===== Sentence Builder (10) =====
{"code": "first_sentence", "category": "sentence", "icon": "📝",
"name_pl": "Debiutant", "name_ru": "Дебютант", "name_uk": "Дебютант", "name_en": "Debutant",
"desc_pl": "Ułóż 1 zdanie", "desc_ru": "Составить 1 предложение", "desc_uk": "Скласти 1 речення", "desc_en": "Build 1 sentence",
"type": "sentences_completed", "value": 1},
{"code": "ten_sentences", "category": "sentence", "icon": "✍️",
"name_pl": "Autor", "name_ru": "Автор", "name_uk": "Автор", "name_en": "Author",
"desc_pl": "Ułóż 10 zdań", "desc_ru": "Составить 10 предложений", "desc_uk": "Скласти 10 речень", "desc_en": "Build 10 sentences",
"type": "sentences_completed", "value": 10},
{"code": "fifty_sentences", "category": "sentence", "icon": "🎓",
"name_pl": "Profesor", "name_ru": "Профессор", "name_uk": "Професор", "name_en": "Professor",
"desc_pl": "Ułóż 50 zdań", "desc_ru": "Составить 50 предложений", "desc_uk": "Скласти 50 речень", "desc_en": "Build 50 sentences",
"type": "sentences_completed", "value": 50},
{"code": "hundred_sentences", "category": "sentence", "icon": "📚",
"name_pl": "Bibliotekarz", "name_ru": "Библиотекарь", "name_uk": "Бібліотекар", "name_en": "Librarian",
"desc_pl": "Ułóż 100 zdań", "desc_ru": "Составить 100 предложений", "desc_uk": "Скласти 100 речень", "desc_en": "Build 100 sentences",
"type": "sentences_completed", "value": 100},
{"code": "sentence_200", "category": "sentence", "icon": "📖",
"name_pl": "Pisarz", "name_ru": "Писатель", "name_uk": "Письменник", "name_en": "Writer",
"desc_pl": "Ułóż 200 zdań", "desc_ru": "Составить 200 предложений", "desc_uk": "Скласти 200 речень", "desc_en": "Build 200 sentences",
"type": "sentences_completed", "value": 200},
{"code": "perfect_sentence_5", "category": "sentence", "icon": "",
"name_pl": "Perfekcjonista", "name_ru": "Перфекционист", "name_uk": "Перфекціоніст", "name_en": "Perfectionist",
"desc_pl": "Ułóż 5 zdań bez błędu", "desc_ru": "Составить 5 предложений без единой ошибки", "desc_uk": "Скласти 5 речень без жодної помилки", "desc_en": "Build 5 sentences without mistakes",
"type": "perfect_sentences", "value": 5},
{"code": "sentence_perfect_10", "category": "sentence", "icon": "💎",
"name_pl": "Mistrz zdań", "name_ru": "Мастер предложений", "name_uk": "Майстер речень", "name_en": "Sentence Master",
"desc_pl": "Ułóż 10 zdań bez błędu", "desc_ru": "Составить 10 предложений без ошибок", "desc_uk": "Скласти 10 речень без помилок", "desc_en": "Build 10 sentences without mistakes",
"type": "perfect_sentences", "value": 10},
{"code": "sentence_streak_10", "category": "sentence", "icon": "💪",
"name_pl": "Nieomylny", "name_ru": "Безошибочный", "name_uk": "Безпомилковий", "name_en": "Flawless",
"desc_pl": "Ułóż 10 zdań z rzędu bez błędu", "desc_ru": "Составить 10 предложений подряд без ошибок", "desc_uk": "Скласти 10 речень поспіль без помилок", "desc_en": "Build 10 sentences in a row without mistakes",
"type": "sentence_streak", "value": 10},
{"code": "speed_10", "category": "sentence", "icon": "🚀",
"name_pl": "Turbo", "name_ru": "Турбо", "name_uk": "Турбо", "name_en": "Turbo",
"desc_pl": "Ułóż zdanie w mniej niż 10 sekund", "desc_ru": "Составить предложение быстрее 10 секунд", "desc_uk": "Скласти речення швидше 10 секунд", "desc_en": "Build a sentence in under 10 seconds",
"type": "speed_sentence", "value": 10},
{"code": "sentence_speed_5", "category": "sentence", "icon": "",
"name_pl": "Ekspres", "name_ru": "Экспресс", "name_uk": "Експрес", "name_en": "Express",
"desc_pl": "Ułóż zdanie w mniej niż 5 sekund", "desc_ru": "Составить предложение быстрее 5 секунд", "desc_uk": "Скласти речення швидше 5 секунд", "desc_en": "Build a sentence in under 5 seconds",
"type": "speed_sentence", "value": 5},
# ===== Word Quiz (8) =====
{"code": "quiz_10", "category": "wordquiz", "icon": "🧠",
"name_pl": "Myśliciel", "name_ru": "Мыслитель", "name_uk": "Мислитель", "name_en": "Thinker",
"desc_pl": "Odgadnij 10 słów w Quiz słownym", "desc_ru": "Угадать 10 слов в Квизе слов", "desc_uk": "Відгадати 10 слів у Квізі слів", "desc_en": "Guess 10 words in Word Quiz",
"type": "quiz_guessed", "value": 10},
{"code": "quiz_50", "category": "wordquiz", "icon": "🎓",
"name_pl": "Mistrz quizu", "name_ru": "Мастер квиза", "name_uk": "Майстер квізу", "name_en": "Quiz Master",
"desc_pl": "Odgadnij 50 słów w Quiz słownym", "desc_ru": "Угадать 50 слов в Квизе слов", "desc_uk": "Відгадати 50 слів у Квізі слів", "desc_en": "Guess 50 words in Word Quiz",
"type": "quiz_guessed", "value": 50},
{"code": "quiz_100", "category": "wordquiz", "icon": "🏆",
"name_pl": "Mózg", "name_ru": "Мозг", "name_uk": "Мозок", "name_en": "Brain",
"desc_pl": "Odgadnij 100 słów w Quiz słownym", "desc_ru": "Угадать 100 слов в Квизе слов", "desc_uk": "Відгадати 100 слів у Квізі слів", "desc_en": "Guess 100 words in Word Quiz",
"type": "quiz_guessed", "value": 100},
{"code": "quiz_perfect_10", "category": "wordquiz", "icon": "💎",
"name_pl": "Idealny wynik", "name_ru": "Идеальный результат", "name_uk": "Ідеальний результат", "name_en": "Perfect Score",
"desc_pl": "Odgadnij 10 słów bez pomyłki", "desc_ru": "Угадать 10 слов без ошибок", "desc_uk": "Відгадати 10 слів без помилок", "desc_en": "Guess 10 words without mistakes",
"type": "quiz_perfect_streak", "value": 10},
{"code": "quiz_streak_20", "category": "wordquiz", "icon": "🔥",
"name_pl": "Niepowstrzymany", "name_ru": "Неудержимый", "name_uk": "Невтримний", "name_en": "Unstoppable",
"desc_pl": "Odgadnij 20 słów z rzędu", "desc_ru": "Угадать 20 слов подряд", "desc_uk": "Відгадати 20 слів поспіль", "desc_en": "Guess 20 words in a row",
"type": "quiz_perfect_streak", "value": 20},
{"code": "quiz_speed", "category": "wordquiz", "icon": "",
"name_pl": "Błyskawica", "name_ru": "Молния", "name_uk": "Блискавка", "name_en": "Lightning",
"desc_pl": "Odgadnij słowo w mniej niż 3 sekundy", "desc_ru": "Угадать слово быстрее 3 секунд", "desc_uk": "Відгадати слово швидше 3 секунд", "desc_en": "Guess a word in under 3 seconds",
"type": "quiz_speed", "value": 3},
{"code": "quiz_speed_2", "category": "wordquiz", "icon": "🚀",
"name_pl": "Super szybki", "name_ru": "Супер быстрый", "name_uk": "Супер швидкий", "name_en": "Super Fast",
"desc_pl": "Odgadnij słowo w mniej niż 2 sekundy", "desc_ru": "Угадать слово быстрее 2 секунд", "desc_uk": "Відгадати слово швидше 2 секунд", "desc_en": "Guess a word in under 2 seconds",
"type": "quiz_speed", "value": 2},
{"code": "quiz_no_mistakes", "category": "wordquiz", "icon": "🏆",
"name_pl": "Bezbłędny", "name_ru": "Безошибочный", "name_uk": "Безпомилковий", "name_en": "Flawless",
"desc_pl": "Odgadnij słowo bez żadnej pomyłki", "desc_ru": "Угадать слово без единой ошибки", "desc_uk": "Відгадати слово без жодної помилки", "desc_en": "Guess a word without any mistakes",
"type": "quiz_no_mistakes", "value": 1},
# ===== Bubble Words (8) =====
{"code": "bubble_5", "category": "bubblewords", "icon": "🫧",
"name_pl": "Początkujący", "name_ru": "Начинающий", "name_uk": "Початківець", "name_en": "Beginner",
"desc_pl": "Ukończ 5 poziomów w Bąbelkach słów", "desc_ru": "Пройти 5 уровней в Пузырях слов", "desc_uk": "Пройти 5 рівнів у Бульбашках слів", "desc_en": "Complete 5 levels in Bubble Words",
"type": "bubble_levels", "value": 5},
{"code": "bubble_25", "category": "bubblewords", "icon": "🌟",
"name_pl": "Bąbelkowy mistrz", "name_ru": "Пузырьковый мастер", "name_uk": "Бульбашковий майстер", "name_en": "Bubble Master",
"desc_pl": "Ukończ 25 poziomów w Bąbelkach słów", "desc_ru": "Пройти 25 уровней в Пузырях слов", "desc_uk": "Пройти 25 рівнів у Бульбашках слів", "desc_en": "Complete 25 levels in Bubble Words",
"type": "bubble_levels", "value": 25},
{"code": "bubble_50", "category": "bubblewords", "icon": "💪",
"name_pl": "Bańkowy wojownik", "name_ru": "Пузырьковый воин", "name_uk": "Бульбашковий воїн", "name_en": "Bubble Warrior",
"desc_pl": "Ukończ 50 poziomów w Bąbelkach słów", "desc_ru": "Пройти 50 уровней в Пузырях слов", "desc_uk": "Пройти 50 рівнів у Бульбашках слів", "desc_en": "Complete 50 levels in Bubble Words",
"type": "bubble_levels", "value": 50},
{"code": "bubble_100", "category": "bubblewords", "icon": "👑",
"name_pl": "Król bąbelków", "name_ru": "Король пузырей", "name_uk": "Король бульбашок", "name_en": "Bubble King",
"desc_pl": "Ukończ 100 poziomów w Bąbelkach słów", "desc_ru": "Пройти 100 уровней в Пузырях слов", "desc_uk": "Пройти 100 рівнів у Бульбашках слів", "desc_en": "Complete 100 levels in Bubble Words",
"type": "bubble_levels", "value": 100},
{"code": "bubble_no_hints", "category": "bubblewords", "icon": "💡",
"name_pl": "Samodzielny", "name_ru": "Самостоятельный", "name_uk": "Самостійний", "name_en": "Independent",
"desc_pl": "Ukończ poziom bez podpowiedzi", "desc_ru": "Пройти уровень без подсказок", "desc_uk": "Пройти рівень без підказок", "desc_en": "Complete a level without hints",
"type": "bubble_no_hints", "value": 1},
{"code": "bubble_perfect", "category": "bubblewords", "icon": "",
"name_pl": "Perfekcyjny", "name_ru": "Идеальный", "name_uk": "Ідеальний", "name_en": "Perfect",
"desc_pl": "Ukończ poziom z maksymalną punktacją (100)", "desc_ru": "Пройти уровень с максимальным счётом (100)", "desc_uk": "Пройти рівень з максимальним рахунком (100)", "desc_en": "Complete a level with maximum score (100)",
"type": "bubble_perfect", "value": 1},
{"code": "bubble_speed", "category": "bubblewords", "icon": "🚀",
"name_pl": "Szybki bańki", "name_ru": "Быстрые пузыри", "name_uk": "Швидкі бульбашки", "name_en": "Fast Bubbles",
"desc_pl": "Ukończ poziom w mniej niż 45 sekund", "desc_ru": "Пройти уровень быстрее 45 секунд", "desc_uk": "Пройти рівень швидше 45 секунд", "desc_en": "Complete a level in under 45 seconds",
"type": "bubble_speed", "value": 45},
{"code": "bubble_speed_30", "category": "bubblewords", "icon": "",
"name_pl": "Bąbelkowa błyskawica", "name_ru": "Пузырьковая молния", "name_uk": "Бульбашкова блискавка", "name_en": "Bubble Lightning",
"desc_pl": "Ukończ poziom w mniej niż 30 sekund", "desc_ru": "Пройти уровень быстрее 30 секунд", "desc_uk": "Пройти рівень швидше 30 секунд", "desc_en": "Complete a level in under 30 seconds",
"type": "bubble_speed", "value": 30},
# ===== General (15) =====
{"code": "score_1000", "category": "general", "icon": "🌟",
"name_pl": "Wschodząca gwiazda", "name_ru": "Восходящая звезда", "name_uk": "Висхідна зірка", "name_en": "Rising Star",
"desc_pl": "Zdobądź 1000 punktów", "desc_ru": "Набрать 1000 очков", "desc_uk": "Набрати 1000 балів", "desc_en": "Earn 1000 points",
"type": "total_score", "value": 1000},
{"code": "score_5000", "category": "general", "icon": "💎",
"name_pl": "Diament", "name_ru": "Бриллиант", "name_uk": "Діамант", "name_en": "Diamond",
"desc_pl": "Zdobądź 5000 punktów", "desc_ru": "Набрать 5000 очков", "desc_uk": "Набрати 5000 балів", "desc_en": "Earn 5000 points",
"type": "total_score", "value": 5000},
{"code": "score_10000", "category": "general", "icon": "💯",
"name_pl": "Legenda", "name_ru": "Легенда", "name_uk": "Легенда", "name_en": "Legend",
"desc_pl": "Zdobądź 10000 punktów", "desc_ru": "Набрать 10000 очков", "desc_uk": "Набрати 10000 балів", "desc_en": "Earn 10000 points",
"type": "total_score", "value": 10000},
{"code": "score_25000", "category": "general", "icon": "🦄",
"name_pl": "Jednorożec", "name_ru": "Единорог", "name_uk": "Єдиноріг", "name_en": "Unicorn",
"desc_pl": "Zdobądź 25000 punktów", "desc_ru": "Набрать 25000 очков", "desc_uk": "Набрати 25000 балів", "desc_en": "Earn 25000 points",
"type": "total_score", "value": 25000},
{"code": "rank_1", "category": "general", "icon": "🏆",
"name_pl": "Mistrz", "name_ru": "Чемпион", "name_uk": "Чемпіон", "name_en": "Champion",
"desc_pl": "Zajmij 1 miejsce w rankingu", "desc_ru": "Занять 1 место в рейтинге", "desc_uk": "Зайняти 1 місце в рейтингу", "desc_en": "Reach 1st place on leaderboard",
"type": "rank_reached", "value": 1},
{"code": "rank_top3", "category": "general", "icon": "🥇",
"name_pl": "Medalista", "name_ru": "Призёр", "name_uk": "Призер", "name_en": "Medalist",
"desc_pl": "Zajmij miejsce w top 3", "desc_ru": "Попасть в топ-3 рейтинга", "desc_uk": "Потрапити в топ-3 рейтингу", "desc_en": "Reach top 3 on leaderboard",
"type": "rank_reached", "value": 3},
{"code": "login_3", "category": "general", "icon": "📊",
"name_pl": "Stały gracz", "name_ru": "Постоянный игрок", "name_uk": "Постійний гравець", "name_en": "Regular Player",
"desc_pl": "Zaloguj się 3 dni z rzędu", "desc_ru": "Заходить 3 дня подряд", "desc_uk": "Заходити 3 дні поспіль", "desc_en": "Log in 3 days in a row",
"type": "login_streak", "value": 3},
{"code": "login_7", "category": "general", "icon": "🔥",
"name_pl": "Tygodniowa seria", "name_ru": "Недельная серия", "name_uk": "Тижнева серія", "name_en": "Weekly Streak",
"desc_pl": "Zaloguj się 7 dni z rzędu", "desc_ru": "Заходить 7 дней подряд", "desc_uk": "Заходити 7 днів поспіль", "desc_en": "Log in 7 days in a row",
"type": "login_streak", "value": 7},
{"code": "change_language", "category": "general", "icon": "🌍",
"name_pl": "Poliglota", "name_ru": "Полиглот", "name_uk": "Поліглот", "name_en": "Polyglot",
"desc_pl": "Zmień język interfejsu", "desc_ru": "Сменить язык интерфейса", "desc_uk": "Змінити мову інтерфейсу", "desc_en": "Change interface language",
"type": "language_changed", "value": 1},
# 🔧 ИСПРАВЛЕНО: Заменено достижение "Открой Грамматику" на "Открой меню учителя"
{"code": "open_teacher_menu", "category": "general", "icon": "🎓",
"name_pl": "Uczeń", "name_ru": "Ученик", "name_uk": "Учень", "name_en": "Student",
"desc_pl": "Otwórz menu nauczyciela", "desc_ru": "Открыть меню учителя", "desc_uk": "Відкрити меню вчителя", "desc_en": "Open the teacher menu",
"type": "teacher_menu_opened", "value": 1},
{"code": "open_cheatsheets", "category": "general", "icon": "📝",
"name_pl": "Ciekawski", "name_ru": "Любознательный", "name_uk": "Допитливий", "name_en": "Curious",
"desc_pl": "Otwórz sekcję Ściągi", "desc_ru": "Открыть раздел Шпаргалки", "desc_uk": "Відкрити розділ Шпаргалки", "desc_en": "Open the Cheatsheets section",
"type": "cheatsheets_opened", "value": 1},
{"code": "open_pronunciation", "category": "general", "icon": "🔊",
"name_pl": "Słuchacz", "name_ru": "Слушатель", "name_uk": "Слухач", "name_en": "Listener",
"desc_pl": "Otwórz sekcję Wymowa", "desc_ru": "Открыть раздел Произношение", "desc_uk": "Відкрити розділ Вимова", "desc_en": "Open the Pronunciation section",
"type": "pronunciation_opened", "value": 1},
{"code": "open_ai_assistant", "category": "general", "icon": "🤖",
"name_pl": "Asystent AI", "name_ru": "AI-помощник", "name_uk": "AI-помічник", "name_en": "AI Assistant",
"desc_pl": "Otwórz Asystenta AI", "desc_ru": "Открыть AI-помощника", "desc_uk": "Відкрити AI-помічника", "desc_en": "Open the AI Assistant",
"type": "ai_assistant_opened", "value": 1},
{"code": "open_alphabet", "category": "general", "icon": "🔤",
"name_pl": "Alfabetysta", "name_ru": "Алфавитист", "name_uk": "Алфавітист", "name_en": "Alphabetist",
"desc_pl": "Otwórz sekcję Alfabet", "desc_ru": "Открыть раздел Алфавит", "desc_uk": "Відкрити розділ Алфавіт", "desc_en": "Open the Alphabet section",
"type": "alphabet_opened", "value": 1},
{"code": "open_rules", "category": "general", "icon": "📐",
"name_pl": "Gramatyk", "name_ru": "Грамматик", "name_uk": "Граматик", "name_en": "Grammarian",
"desc_pl": "Otwórz sekcję Zasady", "desc_ru": "Открыть раздел Правила", "desc_uk": "Відкрити розділ Правила", "desc_en": "Open the Rules section",
"type": "rules_opened", "value": 1},
# 🔧 НОВОЕ: Достижение за открытие Прогресса
{"code": "open_progress", "category": "general", "icon": "📊",
"name_pl": "Planista", "name_ru": "Планировщик", "name_uk": "Планувальник", "name_en": "Planner",
"desc_pl": "Otwórz sekcję Postęp", "desc_ru": "Открыть раздел Прогресс", "desc_uk": "Відкрити розділ Прогрес", "desc_en": "Open the Progress section",
"type": "progress_opened", "value": 1},
]
async def init_achievements():
"""Добавляет новые достижения, не трогая существующие"""
pool = await get_pool()
async with pool.acquire() as conn:
for a in ACHIEVEMENTS_DATA:
exists = await conn.fetchval(
"SELECT 1 FROM achievements WHERE code = $1", a["code"]
)
if not exists:
await conn.execute(
"""INSERT INTO achievements (code, category, icon,
name_pl, name_ru, name_uk, name_en,
description_pl, description_ru, description_uk, description_en,
requirement_type, requirement_value)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)""",
a["code"], a["category"], a["icon"],
a["name_pl"], a["name_ru"], a["name_uk"], a["name_en"],
a["desc_pl"], a["desc_ru"], a["desc_uk"], a["desc_en"],
a["type"], a["value"]
)
else:
# 🔧 НОВОЕ: Обновляем существующие достижения (на случай изменения названий)
await conn.execute("""
UPDATE achievements SET
category = $2,
icon = $3,
name_pl = $4,
name_ru = $5,
name_uk = $6,
name_en = $7,
description_pl = $8,
description_ru = $9,
description_uk = $10,
description_en = $11,
requirement_type = $12,
requirement_value = $13
WHERE code = $1
""", a["code"], a["category"], a["icon"],
a["name_pl"], a["name_ru"], a["name_uk"], a["name_en"],
a["desc_pl"], a["desc_ru"], a["desc_uk"], a["desc_en"],
a["type"], a["value"])
@router.get("")
async def get_all_achievements(request: Request):
"""Получить все достижения"""
await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
rows = await conn.fetch("SELECT * FROM achievements ORDER BY id")
return {
"achievements": [dict(row) for row in rows]
}
@router.get("/user")
async def get_user_achievements(request: Request):
"""Получить достижения пользователя"""
telegram_id, _ = await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
rows = await conn.fetch("""
SELECT a.*,
CASE WHEN ua.user_id IS NOT NULL THEN TRUE ELSE FALSE END as unlocked,
ua.unlocked_at
FROM achievements a
LEFT JOIN user_achievements ua ON a.id = ua.achievement_id AND ua.user_id = $1
ORDER BY a.id
""", telegram_id)
achievements = []
for row in rows:
a = dict(row)
if a.get("unlocked_at"):
a["unlocked_at"] = a["unlocked_at"].isoformat()
achievements.append(a)
total = len(achievements)
unlocked = sum(1 for a in achievements if a["unlocked"])
return {
"achievements": achievements,
"total": total,
"unlocked": unlocked,
"progress_percent": round((unlocked / total * 100), 1) if total > 0 else 0
}
async def check_and_award_achievements(telegram_id: int, achievement_type: str, value: int = 1):
"""
Проверяет и выдаёт достижения пользователю.
Возвращает названия на языке пользователя.
"""
pool = await get_pool()
async with pool.acquire() as conn:
user_lang = await conn.fetchval(
"SELECT app_language FROM users WHERE telegram_id = $1", telegram_id
)
if not user_lang or user_lang not in ["pl", "ru", "uk", "en"]:
user_lang = "pl"
matching = await conn.fetch("""
SELECT a.* FROM achievements a
WHERE a.requirement_type = $1
AND a.requirement_value <= $2
AND a.id NOT IN (
SELECT achievement_id FROM user_achievements WHERE user_id = $3
)
""", achievement_type, value, telegram_id)
newly_unlocked = []
name_field = f"name_{user_lang}"
for ach in matching:
await conn.execute(
"INSERT INTO user_achievements (user_id, achievement_id) VALUES ($1, $2) ON CONFLICT DO NOTHING",
telegram_id, ach["id"]
)
newly_unlocked.append({
"id": ach["id"],
"code": ach["code"],
"icon": ach["icon"],
"name": ach[name_field],
})
return newly_unlocked
@router.post("/track-login")
async def track_login(request: Request):
"""Отмечает ежедневный вход пользователя и проверяет достижения серии"""
telegram_id, _ = await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
await conn.execute(
"INSERT INTO daily_logins (user_id, login_date) VALUES ($1, CURRENT_DATE) ON CONFLICT DO NOTHING",
telegram_id
)
streak = await conn.fetchval("""
WITH dates AS (
SELECT login_date,
login_date - ROW_NUMBER() OVER (ORDER BY login_date DESC)::int AS grp
FROM daily_logins
WHERE user_id = $1
ORDER BY login_date DESC
)
SELECT COUNT(*) FROM dates WHERE grp = (SELECT grp FROM dates LIMIT 1)
""", telegram_id)
streak = streak or 0
new_achievements = await check_and_award_achievements(telegram_id, "login_streak", streak)
return {
"login_streak": streak,
"new_achievements": new_achievements
}
# 🔧 НОВОЕ: Трекинг открытия меню учителя
@router.post("/track-teacher-menu")
async def track_teacher_menu_opened(request: Request):
"""Отмечает открытие меню учителя и выдаёт достижение"""
telegram_id, _ = await require_auth(request)
new_achievements = await check_and_award_achievements(telegram_id, "teacher_menu_opened", 1)
return {
"new_achievements": new_achievements
}
# 🔧 НОВОЕ: Трекинг открытия Прогресса
@router.post("/track-progress")
async def track_progress_opened(request: Request):
"""Отмечает открытие раздела Прогресс и выдаёт достижение"""
telegram_id, _ = await require_auth(request)
new_achievements = await check_and_award_achievements(telegram_id, "progress_opened", 1)
return {
"new_achievements": new_achievements
}
@router.post("/track-cheatsheets")
async def track_cheatsheets_opened(request: Request):
"""Отмечает открытие раздела Шпаргалки и выдаёт достижение"""
telegram_id, _ = await require_auth(request)
new_achievements = await check_and_award_achievements(telegram_id, "cheatsheets_opened", 1)
return {
"new_achievements": new_achievements
}
@router.post("/track-pronunciation")
async def track_pronunciation_opened(request: Request):
"""Отмечает открытие раздела Произношение и выдаёт достижение"""
telegram_id, _ = await require_auth(request)
new_achievements = await check_and_award_achievements(telegram_id, "pronunciation_opened", 1)
return {
"new_achievements": new_achievements
}
@router.post("/track-ai-assistant")
async def track_ai_assistant_opened(request: Request):
"""Отмечает открытие AI-помощника и выдаёт достижение"""
telegram_id, _ = await require_auth(request)
new_achievements = await check_and_award_achievements(telegram_id, "ai_assistant_opened", 1)
return {
"new_achievements": new_achievements
}
@router.post("/track-alphabet")
async def track_alphabet_opened(request: Request):
"""Отмечает открытие раздела Алфавит и выдаёт достижение"""
telegram_id, _ = await require_auth(request)
new_achievements = await check_and_award_achievements(telegram_id, "alphabet_opened", 1)
return {
"new_achievements": new_achievements
}
@router.post("/track-rules")
async def track_rules_opened(request: Request):
"""Отмечает открытие раздела Правила и выдаёт достижение"""
telegram_id, _ = await require_auth(request)
new_achievements = await check_and_award_achievements(telegram_id, "rules_opened", 1)
return {
"new_achievements": new_achievements
}
+148
View File
@@ -0,0 +1,148 @@
"""
Admin Router — админ-панель с реальной статистикой проекта.
1. /admin/dashboard — общая статистика (юзеры, онлайн, по уровням)
2. /admin/sentry-errors — ошибки из Sentry (через API)
"""
import os
import logging
import httpx
from fastapi import APIRouter, Request, HTTPException
from auth import require_auth
from database import get_pool
from config import ADMIN_IDS
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/admin", tags=["admin"])
# ============ Настройки Sentry ============
SENTRY_AUTH_TOKEN = os.getenv("SENTRY_AUTH_TOKEN", "")
SENTRY_ORG_SLUG = os.getenv("SENTRY_ORG_SLUG", "drobysh")
SENTRY_PROJECT_SLUG = os.getenv("SENTRY_PROJECT_SLUG", "filwords-backend")
SENTRY_API_BASE = os.getenv("SENTRY_API_BASE", "https://de.sentry.io/api/0")
def _require_admin(telegram_id: int):
"""Проверка, что пользователь — админ."""
if telegram_id not in ADMIN_IDS:
raise HTTPException(403, "Access denied")
@router.get("/dashboard")
async def get_admin_dashboard(request: Request):
"""
Общая статистика проекта:
- Всего пользователей / новых сегодня / онлайн
- Активных за 24 часа
- Распределение по уровням языка
"""
telegram_id, _ = await require_auth(request)
_require_admin(telegram_id)
pool = await get_pool()
async with pool.acquire() as conn:
# Всего пользователей
total_users = await conn.fetchval("SELECT COUNT(*) FROM users") or 0
# Новых сегодня
new_today = await conn.fetchval(
"SELECT COUNT(*) FROM users WHERE created_at::date = CURRENT_DATE"
) or 0
# Онлайн за последние 30 минут
online_now = await conn.fetchval("""
SELECT COUNT(DISTINCT user_id) FROM (
SELECT user_id FROM user_progress WHERE completed_at > NOW() - INTERVAL '30 minutes'
UNION
SELECT user_id FROM sentence_progress WHERE completed_at > NOW() - INTERVAL '30 minutes'
UNION
SELECT user_id FROM daily_logins WHERE login_date = CURRENT_DATE
) active
""") or 0
# Активных за 24 часа
active_24h = await conn.fetchval("""
SELECT COUNT(DISTINCT user_id) FROM (
SELECT user_id FROM user_progress WHERE completed_at > NOW() - INTERVAL '24 hours'
UNION
SELECT user_id FROM sentence_progress WHERE completed_at > NOW() - INTERVAL '24 hours'
UNION
SELECT user_id FROM user_learning_answers WHERE created_at > NOW() - INTERVAL '24 hours'
UNION
SELECT user_id FROM daily_logins WHERE login_date = CURRENT_DATE
) active
""") or 0
# Распределение по уровням
level_counts = {}
for lvl in ["A1", "A2", "B1", "B2", "C1", "C2"]:
count = await conn.fetchval(
"SELECT COUNT(*) FROM users WHERE current_level = $1", lvl
) or 0
level_counts[lvl] = count
return {
"total_users": total_users,
"new_today": new_today,
"online_now": online_now,
"active_24h": active_24h,
"level_counts": level_counts,
}
@router.get("/sentry-errors")
async def get_sentry_errors(request: Request):
"""
Последние ошибки из Sentry.
Возвращает только unresolved (нерешённые) за последние 24 часа.
"""
telegram_id, _ = await require_auth(request)
_require_admin(telegram_id)
if not SENTRY_AUTH_TOKEN:
return {"errors": [], "error": "SENTRY_AUTH_TOKEN not configured"}
url = f"{SENTRY_API_BASE}/projects/{SENTRY_ORG_SLUG}/{SENTRY_PROJECT_SLUG}/issues/"
params = {
"query": "is:unresolved",
"statsPeriod": "24h",
"limit": 20,
}
headers = {
"Authorization": f"Bearer {SENTRY_AUTH_TOKEN}",
}
try:
async with httpx.AsyncClient(timeout=10.0) as client:
response = await client.get(url, params=params, headers=headers)
if response.status_code != 200:
logger.error(f"Sentry API error: {response.status_code}{response.text[:200]}")
return {"errors": [], "error": f"Sentry API: {response.status_code}"}
raw_issues = response.json()
errors = []
for issue in raw_issues:
errors.append({
"id": issue.get("id"),
"short_id": issue.get("shortId"),
"title": issue.get("title"),
"culprit": issue.get("culprit"),
"level": issue.get("level"),
"count": int(issue.get("count", 0)),
"user_count": issue.get("userCount", 0),
"first_seen": issue.get("firstSeen"),
"last_seen": issue.get("lastSeen"),
"permalink": issue.get("permalink"),
"priority": issue.get("priority"),
})
return {"errors": errors, "count": len(errors)}
except httpx.TimeoutException:
logger.error("Sentry API timeout")
return {"errors": [], "error": "Sentry API timeout"}
except Exception as e:
logger.error(f"Sentry API unexpected error: {e}")
return {"errors": [], "error": str(e)}
File diff suppressed because it is too large Load Diff
+480
View File
@@ -0,0 +1,480 @@
"""
AI Assistant Router — чат-помощник с историей, контекстом, лимитами и статистикой.
Лёгкие запросы → GPT-OSS 20B, сложные → GPT-OSS 120B.
Бесплатный лимит: 5 сообщений в день (кроме админов).
"""
import logging
import time
import json
import httpx
from fastapi import APIRouter, Request, HTTPException
from auth import require_auth
from config import TOGETHER_API_KEY, ADMIN_IDS
from database import get_pool
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/assistant", tags=["assistant"])
TOGETHER_API_URL = "https://api.together.xyz/v1/chat/completions"
# Модели
EASY_MODEL = "openai/gpt-oss-20b"
HARD_MODEL = "openai/gpt-oss-120b"
# Максимум сообщений в истории (10 последних)
MAX_HISTORY = 10
# Бесплатный лимит сообщений в день
FREE_DAILY_LIMIT = 5
LEVELS = ["A1", "A2", "B1", "B2", "C1", "C2"]
SYSTEM_PROMPT = """Ты — AI-помощник Filwords PL, приложения для изучения польского языка.
Твои задачи:
1. Переводить фразы с русского/украинского/английского на польский и обратно
2. Объяснять грамматику польского языка простыми словами
3. Отвечать на вопросы о польском языке
4. Давать примеры использования слов и фраз
5. Помогать с повседневными фразами (магазин, врач, работа, учёба)
ОПРЕДЕЛЕНИЕ ЯЗЫКА ПОЛЬЗОВАТЕЛЯ:
- Если пользователь пишет на русском — отвечай на русском
- Если на украинском — отвечай на украинском
- Если на польском — отвечай на польском
- Если на английском — отвечай на английском
- ВСЕГДА отвечай на языке пользователя!
- Польские слова и фразы в примерах оставляй на польском, но объяснения и комментарии — на языке пользователя
ВАЖНЫЕ ПРАВИЛА:
- НЕ используй markdown (никаких **, *, #, `, ```)
- Пиши простым текстом
- Для перевода используй формат: Польская фраза → Перевод
- Будь кратким, но полезным
- Отвечай на языке пользователя
- Не упоминай, что ты AI или модель. Ты — помощник Filwords."""
# Ключевые слова для сложных запросов (на 4 языках)
HARD_KEYWORDS = [
# Русские
"объясни", "почему", "грамматик", "правило", "разница", "расскажи",
"подробно", "история", "примеры", "спряжение", "склонение", "падеж",
"урок", "мини-урок", "занятие", "обучение",
# Украинские
"поясни", "чому", "граматик", "правило", "різниця", "розкажи",
"докладно", "історія", "приклади", "відмінювання", "відмінок",
"урок", "міні-урок", "заняття", "навчання",
# Польские
"wytłumacz", "dlaczego", "gramatyk", "zasada", "różnica", "opowiedz",
"szczegółowo", "historia", "przykłady", "koniugacja", "deklinacja", "przypadek",
"lekcja", "mini-lekcja", "zajęcia", "nauka",
# Английские
"explain", "why", "grammar", "rule", "difference", "tell me",
"detailed", "history", "examples", "conjugation", "declension", "case",
"lesson", "mini-lesson", "class", "learning",
]
# Ключевые слова для лёгких запросов
EASY_KEYWORDS = [
# Русские
"переведи", "как сказать", "что значит", "привет", "спасибо", "пока",
# Украинские
"переклади", "як сказати", "що означає", "привіт", "дякую", "бувай",
# Польские
"przetłumacz", "jak powiedzieć", "co znaczy", "cześć", "dziękuję",
# Английские
"translate", "how to say", "what does", "hello", "thank you", "bye",
]
def get_model_for_message(message: str) -> str:
"""Определяет какая модель нужна: лёгкая или сложная"""
msg_lower = message.lower()
if any(kw in msg_lower for kw in HARD_KEYWORDS):
logger.info(f"Using HARD model for message: {message[:50]}...")
return HARD_MODEL
if len(message) > 150:
logger.info(f"Using HARD model (long message, {len(message)} chars)")
return HARD_MODEL
logger.info(f"Using EASY model for message: {message[:50]}...")
return EASY_MODEL
def clean_response(text: str) -> str:
"""Убирает markdown-символы из ответа"""
text = text.replace("**", "").replace("*", "")
text = text.replace("###", "").replace("##", "").replace("#", "")
text = text.replace("```", "").replace("`", "")
return text.strip()
async def save_message(user_id: int, message: str, is_user: bool):
"""Сохраняет сообщение в БД и удаляет старые (оставляет только 10 последних)"""
pool = await get_pool()
async with pool.acquire() as conn:
await conn.execute(
"""INSERT INTO chat_history (user_id, message, is_user) VALUES ($1, $2, $3)""",
user_id, message, is_user
)
await conn.execute("""
DELETE FROM chat_history
WHERE user_id = $1
AND id NOT IN (
SELECT id FROM chat_history
WHERE user_id = $1
ORDER BY id DESC
LIMIT $2
)
""", user_id, MAX_HISTORY)
async def get_history(user_id: int) -> list:
"""Получает историю чата (последние 10 сообщений)"""
pool = await get_pool()
async with pool.acquire() as conn:
rows = await conn.fetch("""
SELECT message, is_user FROM chat_history
WHERE user_id = $1
ORDER BY id ASC
""", user_id)
return [{"role": "user" if row["is_user"] else "assistant", "content": row["message"]} for row in rows]
async def get_daily_usage(user_id: int) -> int:
"""Получает количество сообщений за сегодня"""
pool = await get_pool()
async with pool.acquire() as conn:
count = await conn.fetchval("""
SELECT message_count FROM chat_usage
WHERE user_id = $1 AND usage_date = CURRENT_DATE
""", user_id)
return count or 0
async def increment_daily_usage(user_id: int):
"""Увеличивает счётчик сообщений за сегодня"""
pool = await get_pool()
async with pool.acquire() as conn:
await conn.execute("""
INSERT INTO chat_usage (user_id, usage_date, message_count)
VALUES ($1, CURRENT_DATE, 1)
ON CONFLICT (user_id, usage_date)
DO UPDATE SET message_count = chat_usage.message_count + 1
""", user_id)
@router.post("/chat")
async def chat(request: Request):
"""Принимает сообщение пользователя и возвращает ответ AI"""
telegram_id, _ = await require_auth(request)
if not TOGETHER_API_KEY:
raise HTTPException(500, "AI API key not configured")
body = await request.json()
user_message = body.get("message", "").strip()
if not user_message:
raise HTTPException(400, "Message is required")
if len(user_message) > 500:
raise HTTPException(400, "Message too long (max 500 characters)")
is_admin = telegram_id in ADMIN_IDS
if not is_admin:
daily_usage = await get_daily_usage(telegram_id)
if daily_usage >= FREE_DAILY_LIMIT:
return {
"limit_exceeded": True,
"daily_limit": FREE_DAILY_LIMIT,
"used": daily_usage,
"remaining": 0,
"response": "Dziś wykorzystałeś już wszystkie darmowe zapytania. Wróć jutro!",
}
await save_message(telegram_id, user_message, True)
if not is_admin:
await increment_daily_usage(telegram_id)
history = await get_history(telegram_id)
model = get_model_for_message(user_message)
logger.info(f"Assistant chat: user={telegram_id}, model={model}, msg_len={len(user_message)}, history={len(history)}")
start_time = time.time()
try:
async with httpx.AsyncClient(timeout=45.0) as client:
response = await client.post(
TOGETHER_API_URL,
headers={
"Authorization": f"Bearer {TOGETHER_API_KEY}",
"Content-Type": "application/json",
},
json={
"model": model,
"messages": [
{"role": "system", "content": SYSTEM_PROMPT},
*history,
],
"max_tokens": 2500,
"temperature": 0.7,
},
)
elapsed_time = time.time() - start_time
logger.info(f"Assistant response: status={response.status_code}, time={elapsed_time:.2f}s")
if response.status_code != 200:
logger.error(f"AI API error: {response.status_code}, body={response.text[:200]}")
raise HTTPException(500, f"AI API error: {response.status_code}")
data = response.json()
ai_response = data["choices"][0]["message"]["content"].strip()
ai_response = clean_response(ai_response)
await save_message(telegram_id, ai_response, False)
remaining = FREE_DAILY_LIMIT if is_admin else max(0, FREE_DAILY_LIMIT - await get_daily_usage(telegram_id))
logger.info(f"Assistant answer: len={len(ai_response)}, model={model}")
return {
"response": ai_response,
"model_used": model,
"limit_exceeded": False,
"daily_limit": FREE_DAILY_LIMIT if not is_admin else None,
"remaining": remaining if not is_admin else None,
}
except HTTPException:
raise
except httpx.TimeoutException:
logger.error(f"AI API timeout after {time.time() - start_time:.2f}s")
raise HTTPException(500, "AI service timeout")
except Exception as e:
logger.error(f"AI service error: {str(e)}")
raise HTTPException(500, f"AI service error: {str(e)}")
@router.post("/save-analytics")
async def save_analytics(request: Request):
"""Сохраняет ответ аналитики в историю чата"""
telegram_id, _ = await require_auth(request)
body = await request.json()
message = body.get("message", "").strip()
if not message:
raise HTTPException(400, "Message is required")
await save_message(telegram_id, message, False)
return {"status": "ok"}
@router.get("/stats/personal")
async def get_personal_stats(request: Request):
"""Возвращает личную статистику пользователя для AI"""
telegram_id, _ = await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
user = await conn.fetchrow("SELECT current_level, app_language FROM users WHERE telegram_id = $1", telegram_id)
if not user:
raise HTTPException(401, "User not registered")
current_level = user["current_level"] or "A1"
# Прогресс по каждому уровню
level_progress = {}
total_score = 0
total_achievements = await conn.fetchval("""
SELECT COUNT(*) FROM user_achievements WHERE user_id = $1
""", telegram_id) or 0
for level in LEVELS:
# Филворды
ws_total = await conn.fetchval("SELECT COUNT(*) FROM levels WHERE level = $1", level) or 0
ws_completed = await conn.fetchval(
"SELECT COUNT(DISTINCT level_id) FROM user_progress WHERE user_id = $1 AND level = $2 AND completed_at IS NOT NULL",
telegram_id, level
) or 0
ws_percent = round((ws_completed / ws_total * 100), 1) if ws_total > 0 else 0
# Предложения
s_total = await conn.fetchval("SELECT COUNT(*) FROM sentences WHERE level = $1", level) or 0
s_completed = await conn.fetchval(
"SELECT COUNT(*) FROM sentence_progress WHERE user_id = $1 AND level = $2 AND completed_at IS NOT NULL",
telegram_id, level
) or 0
s_percent = round((s_completed / s_total * 100), 1) if s_total > 0 else 0
# Очки уровня
ws_score = await conn.fetchval(
"SELECT COALESCE(SUM(score), 0) FROM user_progress WHERE user_id = $1 AND level = $2 AND completed_at IS NOT NULL",
telegram_id, level
) or 0
s_score = await conn.fetchval(
"SELECT COALESCE(SUM(score), 0) FROM sentence_progress WHERE user_id = $1 AND level = $2 AND completed_at IS NOT NULL",
telegram_id, level
) or 0
wq_score = await conn.fetchval(
"SELECT COALESCE(total_score, 0) FROM word_quiz_progress WHERE user_id = $1 AND level = $2",
telegram_id, level
) or 0
bw_score = await conn.fetchval(
"SELECT COALESCE(total_score, 0) FROM bubble_words_progress WHERE user_id = $1 AND level = $2",
telegram_id, level
) or 0
level_total = ws_score + s_score + wq_score + bw_score
total_score += level_total
level_progress[level] = {
"wordsearch_percent": ws_percent,
"wordsearch_completed": ws_completed,
"wordsearch_total": ws_total,
"sentence_percent": s_percent,
"sentence_completed": s_completed,
"sentence_total": s_total,
"total_score": level_total,
}
# Квиз и пузыри общие
wq_words = await conn.fetchval(
"SELECT COALESCE(words_guessed, 0) FROM word_quiz_progress WHERE user_id = $1",
telegram_id
) or 0
bw_levels = await conn.fetchval(
"SELECT COALESCE(levels_completed, 0) FROM bubble_words_progress WHERE user_id = $1",
telegram_id
) or 0
# Уроки
completed_topics = await conn.fetchval(
"SELECT COUNT(*) FROM user_topic_progress WHERE user_id = $1 AND test_completed = TRUE",
telegram_id
) or 0
return {
"current_level": current_level,
"total_score": total_score,
"achievements": total_achievements,
"words_guessed": wq_words,
"bubble_levels": bw_levels,
"completed_topics": completed_topics,
"level_progress": level_progress,
}
@router.get("/stats/admin")
async def get_admin_stats(request: Request):
"""Возвращает админ-статистику"""
telegram_id, _ = await require_auth(request)
if telegram_id not in ADMIN_IDS:
raise HTTPException(403, "Access denied")
pool = await get_pool()
async with pool.acquire() as conn:
total_users = await conn.fetchval("SELECT COUNT(*) FROM users") or 0
new_today = await conn.fetchval("SELECT COUNT(*) FROM users WHERE created_at::date = CURRENT_DATE") or 0
online_now = await conn.fetchval("""
SELECT COUNT(DISTINCT user_id) FROM (
SELECT user_id FROM user_progress WHERE completed_at > NOW() - INTERVAL '30 minutes'
UNION
SELECT user_id FROM sentence_progress WHERE completed_at > NOW() - INTERVAL '30 minutes'
UNION
SELECT user_id FROM daily_logins WHERE login_date = CURRENT_DATE
) active
""") or 0
# Языки
language_counts = {}
for lang in ["pl", "ru", "uk", "en"]:
count = await conn.fetchval("SELECT COUNT(*) FROM users WHERE app_language = $1", lang) or 0
language_counts[lang] = count
# Уровни
level_counts = {}
for level in LEVELS:
count = await conn.fetchval("SELECT COUNT(*) FROM users WHERE current_level = $1", level) or 0
level_counts[level] = count
return {
"total_users": total_users,
"new_today": new_today,
"online_now": online_now,
"language_counts": language_counts,
"level_counts": level_counts,
}
@router.get("/limit")
async def get_limit(request: Request):
"""Возвращает информацию о лимите пользователя"""
telegram_id, _ = await require_auth(request)
is_admin = telegram_id in ADMIN_IDS
if is_admin:
return {
"is_admin": True,
"daily_limit": None,
"used": 0,
"remaining": None,
}
daily_usage = await get_daily_usage(telegram_id)
remaining = max(0, FREE_DAILY_LIMIT - daily_usage)
return {
"is_admin": False,
"daily_limit": FREE_DAILY_LIMIT,
"used": daily_usage,
"remaining": remaining,
}
@router.get("/history")
async def get_chat_history(request: Request):
"""Возвращает историю чата пользователя"""
telegram_id, _ = await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
rows = await conn.fetch("""
SELECT message, is_user, created_at FROM chat_history
WHERE user_id = $1
ORDER BY id ASC
""", telegram_id)
history = []
for row in rows:
history.append({
"message": row["message"],
"is_user": row["is_user"],
"created_at": row["created_at"].isoformat() if row["created_at"] else None,
})
return {"history": history}
@router.post("/history/clear")
async def clear_chat_history(request: Request):
"""Очищает историю чата пользователя"""
telegram_id, _ = await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
await conn.execute("DELETE FROM chat_history WHERE user_id = $1", telegram_id)
return {"status": "ok"}
+274
View File
@@ -0,0 +1,274 @@
import random
import time
import hashlib # 🔧 НОВОЕ: импорт hashlib для стабильного хэширования
from fastapi import APIRouter, Request, HTTPException
from auth import require_auth
from database import get_pool
router = APIRouter(prefix="/bubblewords", tags=["bubblewords"])
def split_word_into_parts(word: str, forbidden_parts: set = None) -> list:
"""Разбивает польское слово на части по 1-3 буквы.
forbidden_parts — части, которые уже заняты другими словами на уровне.
Если не удалось найти уникальное разбиение — добавляет суффикс с индексом слова."""
if forbidden_parts is None:
forbidden_parts = set()
n = len(word)
attempts = 0
max_attempts = 50 # Увеличиваем попытки
while attempts < max_attempts:
parts = []
i = 0
while i < n:
remaining = n - i
if remaining == 1:
parts.append(word[i:])
i += 1
elif remaining == 2:
if random.random() < 0.4:
parts.append(word[i:i+1])
parts.append(word[i+1:])
else:
parts.append(word[i:])
i += 2
elif remaining == 3:
choice = random.choice([1, 2, 3])
if choice == 1:
parts.append(word[i:i+1])
parts.append(word[i+1:])
elif choice == 2:
parts.append(word[i:i+2])
parts.append(word[i+2:])
else:
parts.append(word[i:])
i += 3
elif remaining == 4:
choice = random.choice([(2, 2), (1, 3), (3, 1), (1, 1, 2), (2, 1, 1)])
for size in choice:
parts.append(word[i:i+size])
i += size
elif remaining == 5:
choice = random.choice([(2, 3), (3, 2), (2, 2, 1), (2, 1, 2)])
for size in choice:
parts.append(word[i:i+size])
i += size
elif remaining == 6:
choice = random.choice([(3, 3), (2, 2, 2), (3, 2, 1), (2, 3, 1)])
for size in choice:
parts.append(word[i:i+size])
i += size
else:
size = random.choice([2, 3])
if i + size > n:
size = n - i
parts.append(word[i:i+size])
i += size
# Проверяем конфликт
conflict = False
for part in parts:
if part in forbidden_parts:
conflict = True
break
if not conflict:
return parts
attempts += 1
# Если за 50 попыток не нашли уникальное разбиение —
# разбиваем слово ПО БУКВАМ и добавляем суффикс с хешем слова
# Такой суффикс гарантированно уникален для каждого слова
# 🔧 ИСПРАВЛЕНО: заменён hash() на hashlib.md5 для стабильности между запусками
word_hash = hashlib.md5(word.encode('utf-8')).hexdigest()[:4] # Берём первые 4 символа MD5
unique_parts = []
for idx, letter in enumerate(word):
unique_parts.append(f"{letter}.{word_hash}.{idx}")
return unique_parts
@router.get("/next")
async def get_next_bubble_level(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"
progress = await conn.fetchrow(
"SELECT levels_completed FROM bubble_words_progress WHERE user_id = $1",
telegram_id
)
current_bubble_level = (progress["levels_completed"] if progress else 0) + 1
# Прогрессия по количеству слов на уровне
if current_bubble_level <= 10:
num_words = random.choice([3, 4])
elif current_bubble_level <= 25:
num_words = random.choice([5, 6])
else:
num_words = random.choice([7, 8])
# Запрос слов с фильтром по уровню и level_id IS NULL
words = await conn.fetch("""
SELECT word_pl, translation_ru, translation_uk, translation_en
FROM (
SELECT DISTINCT ON (word_pl) word_pl, translation_ru, translation_uk, translation_en
FROM level_words
WHERE level = $1 AND level_id IS NULL
) AS unique_words
ORDER BY RANDOM()
LIMIT $2
""", current_level, num_words)
if len(words) < 2:
raise HTTPException(404, "Not enough words")
translation_field = f"translation_{lang}"
result_words = []
all_parts = []
forbidden_parts = set()
for w in words:
word_pl = w["word_pl"]
parts = split_word_into_parts(word_pl, forbidden_parts)
translation = w.get(translation_field) or w.get("translation_en") or word_pl
result_words.append({
"word_pl": word_pl,
"translation": translation,
"parts": parts,
})
for part in parts:
forbidden_parts.add(part)
all_parts.append({
"text": part,
"word_pl": word_pl,
})
random.shuffle(all_parts)
return {
"words": result_words,
"bubbles": all_parts,
"level": current_bubble_level,
}
@router.post("/complete")
async def complete_bubble_level(request: Request):
"""Завершение уровня пузырей, начисление очков и проверка достижений"""
telegram_id, _ = await require_auth(request)
body = await request.json()
hints_used = body.get("hints_used", 0)
words_count = body.get("words_count", 3)
completion_time = body.get("completion_time")
hints_used = min(max(0, hints_used), 3)
score = max(10, 100 - hints_used * 10)
pool = await get_pool()
async with pool.acquire() as conn:
user_exists = await conn.fetchval(
"SELECT 1 FROM users WHERE telegram_id = $1", telegram_id
)
if not user_exists:
raise HTTPException(401, "User not registered")
# Получаем current_level пользователя
current_level = await conn.fetchval(
"SELECT current_level FROM users WHERE telegram_id = $1", telegram_id
) or "A1"
await conn.execute("""
INSERT INTO bubble_words_progress (user_id, levels_completed, total_score, level)
VALUES ($1, 1, $2, $3)
ON CONFLICT (user_id) DO UPDATE
SET levels_completed = bubble_words_progress.levels_completed + 1,
total_score = bubble_words_progress.total_score + $2,
level = $3
""", telegram_id, score, current_level)
# Проверка достижений
from routers.achievements import check_and_award_achievements
new_achievements = []
total_levels = await conn.fetchval(
"SELECT levels_completed FROM bubble_words_progress WHERE user_id = $1",
telegram_id
) or 0
ach = await check_and_award_achievements(telegram_id, "bubble_levels", total_levels)
new_achievements.extend(ach)
if hints_used == 0:
ach = await check_and_award_achievements(telegram_id, "bubble_no_hints", 1)
new_achievements.extend(ach)
if score == 100:
ach = await check_and_award_achievements(telegram_id, "bubble_perfect", 1)
new_achievements.extend(ach)
if completion_time is not None and completion_time > 0 and int(completion_time) <= 45:
ach = await check_and_award_achievements(telegram_id, "bubble_speed", 45)
new_achievements.extend(ach)
wordsearch_score = await conn.fetchval(
"SELECT COALESCE(SUM(score), 0) FROM user_progress WHERE user_id = $1 AND completed_at IS NOT NULL",
telegram_id
) or 0
sentence_score = await conn.fetchval(
"SELECT COALESCE(SUM(score), 0) FROM sentence_progress WHERE user_id = $1 AND completed_at IS NOT NULL",
telegram_id
) or 0
wordquiz_score = await conn.fetchval(
"SELECT COALESCE(total_score, 0) FROM word_quiz_progress WHERE user_id = $1",
telegram_id
) or 0
bubblewords_score = await conn.fetchval(
"SELECT COALESCE(total_score, 0) FROM bubble_words_progress WHERE user_id = $1",
telegram_id
) or 0
grand_total = wordsearch_score + sentence_score + wordquiz_score + bubblewords_score
ach = await check_and_award_achievements(telegram_id, "total_score", grand_total)
new_achievements.extend(ach)
return {
"status": "ok",
"score": score,
"completion_time": completion_time,
"new_achievements": new_achievements,
}
@router.get("/progress")
async def get_bubble_progress(request: Request):
"""Статистика пользователя в Bubble Words"""
telegram_id, _ = await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
progress = await conn.fetchrow(
"SELECT levels_completed, total_score FROM bubble_words_progress WHERE user_id = $1",
telegram_id
)
return {
"levels_completed": progress["levels_completed"] if progress else 0,
"total_score": progress["total_score"] if progress else 0,
}
+725
View File
@@ -0,0 +1,725 @@
"""
Feed Router — посты для карусели (новости / партнёры / контакты / приложения).
Публичные эндпоинты:
- GET /feed/{section}?lang={lang} — лента постов с локализацией
- GET /feed/post/{id}?lang={lang} — один пост
Админские эндпоинты:
- POST /admin/feed/post — создать пост + автоперевод на 4 языка
- PUT /admin/feed/post/{id} — редактировать (опционально перевести заново)
- POST /admin/feed/post/{id}/delete — удалить (вложения удалит CASCADE)
- POST /admin/feed/upload-image — загрузить картинку (multipart, до 3 на пост)
- POST /admin/feed/translate — перевести title+body вручную (без сохранения)
Логика локализации:
- В БД хранятся 4 версии поста (title_pl/ru/uk/en, body_pl/ru/uk/en).
- Юзер получает только свою версию.
- Fallback: _{lang} → _en → _pl.
"""
import logging
import json
import re
import uuid
import os
from pathlib import Path
from typing import Optional, Any
import httpx
from fastapi import APIRouter, Request, HTTPException, UploadFile, File, Form
from auth import require_auth
from database import get_pool
from config import (
TOGETHER_API_KEY,
ADMIN_IDS,
FEED_SECTIONS,
FEED_TARGET_LANGUAGES,
MAX_FEED_ATTACHMENTS,
MAX_FEED_IMAGE_SIZE_BYTES,
FEED_ALLOWED_IMAGE_TYPES,
UPLOADS_DIR,
)
logger = logging.getLogger(__name__)
router = APIRouter(tags=["feed"])
TOGETHER_API_URL = "https://api.together.xyz/v1/chat/completions"
TRANSLATE_MODEL = "MiniMaxAI/MiniMax-M3"
# 🔧 ФИКС: бэк отдаёт полный URL картинки, чтобы фронт не гадал,
# куда склеивать относительный путь /uploads/feed/...
API_PUBLIC_URL = "https://api.filwordspl.com"
# ============ HELPERS ============
def _require_admin(telegram_id: int) -> None:
"""Проверка, что пользователь — админ. Бросает 403 если нет."""
if telegram_id not in ADMIN_IDS:
raise HTTPException(403, "Access denied")
def _localized_field(row: dict, field: str, lang: str) -> str:
"""
Достаёт локализованное поле из строки БД.
Fallback: _{lang} → _en → _pl → ''.
"""
if not row:
return ""
return (
row.get(f"{field}_{lang}")
or row.get(f"{field}_en")
or row.get(f"{field}_pl")
or ""
)
def _serialize_post(row: dict, attachments: list, lang: str) -> dict:
"""
Собирает финальный объект поста для фронта:
локализованный title/body, ссылки, метаданные, картинки.
"""
return {
"id": row["id"],
"section": row["section"],
"title": _localized_field(row, "title", lang),
"body": _localized_field(row, "body", lang),
"images": [
f"{API_PUBLIC_URL}{a['file_path']}" if a["file_path"].startswith("/") else a["file_path"]
for a in attachments
],
"linkUrl": row.get("link_url"),
"telegramUrl": row.get("telegram_url"),
"createdAt": row["created_at"].isoformat() if row.get("created_at") else None,
"updatedAt": row["updated_at"].isoformat() if row.get("updated_at") else None,
"expiresAt": row["expires_at"].isoformat() if row.get("expires_at") else None,
"isPinned": bool(row.get("is_pinned")),
}
# ============ ПУБЛИЧНЫЕ ЭНДПОИНТЫ ============
@router.get("/feed/{section}")
async def get_feed_section(request: Request, section: str, lang: Optional[str] = None):
"""
Возвращает ленту постов для секции (news / partners / contacts / apps).
- Закреплённые посты — сверху.
- Остальные — по created_at DESC.
- Истёкшие (expires_at < NOW()) — НЕ отдаём.
- Опубликованные (is_published = TRUE) — только они.
"""
telegram_id, _ = await require_auth(request)
if section not in FEED_SECTIONS:
raise HTTPException(400, f"Invalid section: {section}")
pool = await get_pool()
async with pool.acquire() as conn:
# Если lang не передан — берём из профиля юзера
if not lang or lang not in FEED_TARGET_LANGUAGES:
lang = await conn.fetchval(
"SELECT app_language FROM users WHERE telegram_id = $1",
telegram_id,
) or "pl"
if lang not in FEED_TARGET_LANGUAGES:
lang = "pl"
# Тянем посты: закреплённые сверху, потом по дате
rows = await conn.fetch("""
SELECT *
FROM feed_posts
WHERE section = $1
AND is_published = TRUE
AND (expires_at IS NULL OR expires_at > NOW())
ORDER BY is_pinned DESC, created_at DESC
""", section)
if not rows:
return {"posts": [], "section": section, "lang": lang}
# Одним запросом тянем все вложения для всех постов (без N+1)
post_ids = [r["id"] for r in rows]
att_rows = await conn.fetch("""
SELECT post_id, file_path, order_number
FROM feed_attachments
WHERE post_id = ANY($1::bigint[])
ORDER BY post_id, order_number ASC
""", post_ids)
# Группируем вложения по post_id
att_by_post: dict[int, list] = {}
for a in att_rows:
att_by_post.setdefault(a["post_id"], []).append(dict(a))
posts = [
_serialize_post(dict(r), att_by_post.get(r["id"], []), lang)
for r in rows
]
return {"posts": posts, "section": section, "lang": lang}
@router.get("/feed/post/{post_id}")
async def get_feed_post(request: Request, post_id: int, lang: Optional[str] = None):
"""Возвращает один пост по ID (с локализацией)."""
telegram_id, _ = await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
if not lang or lang not in FEED_TARGET_LANGUAGES:
lang = await conn.fetchval(
"SELECT app_language FROM users WHERE telegram_id = $1",
telegram_id,
) or "pl"
if lang not in FEED_TARGET_LANGUAGES:
lang = "pl"
row = await conn.fetchrow("""
SELECT * FROM feed_posts WHERE id = $1 AND is_published = TRUE
""", post_id)
if not row:
raise HTTPException(404, "Post not found")
att_rows = await conn.fetch("""
SELECT file_path, order_number
FROM feed_attachments
WHERE post_id = $1
ORDER BY order_number ASC
""", post_id)
return _serialize_post(dict(row), [dict(a) for a in att_rows], lang)
# ============ AI-ПЕРЕВОД ============
async def translate_to_all_languages(
title: str,
body: str,
source_lang: str,
) -> dict:
"""
Переводит title и body с source_lang на все 4 языка (PL/RU/UK/EN).
Возвращает dict с 8 полями:
title_pl, title_ru, title_uk, title_en,
body_pl, body_ru, body_uk, body_en.
Если AI упал или вернул невалидный JSON — возвращает пустой dict.
Caller должен решить, использовать ли fallback (source_lang для всех языков).
"""
if not TOGETHER_API_KEY:
logger.error("TOGETHER_API_KEY not configured — cannot translate")
return {}
if source_lang not in FEED_TARGET_LANGUAGES:
logger.error(f"Invalid source_lang: {source_lang}")
return {}
system_prompt = f"""Ты — переводчик. Переведи title и body на 4 языка: PL, RU, UK, EN.
Язык оригинала: {source_lang}.
Верни СТРОГО JSON без markdown и без пояснений:
{{
"title_pl": "...", "title_ru": "...", "title_uk": "...", "title_en": "...",
"body_pl": "...", "body_ru": "...", "body_uk": "...", "body_en": "..."
}}
ВАЖНО:
- Если source_lang = ru, то title_ru и body_ru оставь БЕЗ изменений.
- Если source_lang = pl, то title_pl и body_pl оставь БЕЗ изменений.
- Если source_lang = uk, то title_uk и body_uk оставь БЕЗ изменений.
- Если source_lang = en, то title_en и body_en оставь БЕЗ изменений.
- Сохраняй переносы строк внутри body (используй \\n в JSON).
- Не добавляй никаких полей кроме указанных 8.
- Если title пустой — оставь все title_* пустыми строками."""
user_prompt = f"title: {title}\nbody: {body}"
try:
async with httpx.AsyncClient(timeout=90.0) as client:
response = await client.post(
TOGETHER_API_URL,
headers={
"Authorization": f"Bearer {TOGETHER_API_KEY}",
"Content-Type": "application/json",
},
json={
"model": TRANSLATE_MODEL,
"messages": [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt},
],
"max_tokens": 2000,
"temperature": 0.3,
},
)
if response.status_code != 200:
logger.error(
f"Translate API error: {response.status_code}, body={response.text[:200]}"
)
return {}
data = response.json()
raw = data["choices"][0]["message"]["content"].strip()
# Если AI обернул в ```json ... ``` — снимаем обёртку
if raw.startswith("```"):
raw = re.sub(r'^```(?:json)?\s*', '', raw)
raw = re.sub(r'\s*```$', '', raw)
# Иногда AI добавляет текст до/после JSON — вырезаем первый {...}
json_match = re.search(r'\{.*\}', raw, re.DOTALL)
if not json_match:
logger.error(f"AI response has no JSON: {raw[:200]}")
return {}
parsed = json.loads(json_match.group(0))
# Валидация: все 8 полей должны быть строками
expected_keys = [
"title_pl", "title_ru", "title_uk", "title_en",
"body_pl", "body_ru", "body_uk", "body_en",
]
result = {}
for key in expected_keys:
value = parsed.get(key, "")
result[key] = str(value) if value is not None else ""
# Гарантия: source_lang остаётся без изменений
result[f"title_{source_lang}"] = title
result[f"body_{source_lang}"] = body
return result
except json.JSONDecodeError as e:
logger.error(f"Failed to parse AI JSON: {e}")
return {}
except Exception as e:
logger.error(f"Translate error: {str(e)}")
return {}
# ============ АДМИНСКИЕ ЭНДПОИНТЫ ============
@router.post("/admin/feed/post")
async def admin_create_feed_post(request: Request):
"""
Создать feed-пост.
Тело запроса (JSON):
{
"section": "news" | "partners" | "contacts" | "apps",
"source_language": "ru" | "pl" | "uk" | "en",
"title": "Заголовок на source_language",
"body": "Текст поста на source_language",
"link_url": "https://..." (опционально),
"telegram_url": "https://t.me/..." (опционально),
"is_pinned": false (опционально),
"expires_at": "2026-10-15T23:59:59Z" (опционально, для partners)
}
Backend автоматически переведёт title/body на 4 языка через MiniMax-M3.
Если перевод упал — сохраняем source_language для всех языков (fallback).
"""
telegram_id, _ = await require_auth(request)
_require_admin(telegram_id)
body = await request.json()
section = body.get("section", "").strip()
source_language = body.get("source_language", "ru").strip().lower()
title = (body.get("title") or "").strip()
post_body = (body.get("body") or "").strip()
link_url = (body.get("link_url") or "").strip() or None
telegram_url = (body.get("telegram_url") or "").strip() or None
is_pinned = bool(body.get("is_pinned", False))
expires_at_raw = body.get("expires_at")
# Валидация
if section not in FEED_SECTIONS:
raise HTTPException(400, f"Invalid section. Allowed: {sorted(FEED_SECTIONS)}")
if source_language not in FEED_TARGET_LANGUAGES:
raise HTTPException(400, f"Invalid source_language. Allowed: {FEED_TARGET_LANGUAGES}")
if not title and not post_body:
raise HTTPException(400, "Either title or body is required")
if len(title) > 200:
raise HTTPException(400, "Title too long (max 200 characters)")
if len(post_body) > 5000:
raise HTTPException(400, "Body too long (max 5000 characters)")
# Парсим expires_at если передан
expires_at = None
if expires_at_raw:
try:
from datetime import datetime, timezone
# Поддерживаем ISO 8601 с 'Z' или без
normalized = expires_at_raw.replace("Z", "+00:00")
expires_at = datetime.fromisoformat(normalized)
if expires_at.tzinfo is None:
expires_at = expires_at.replace(tzinfo=timezone.utc)
except (ValueError, AttributeError):
raise HTTPException(400, "Invalid expires_at format (use ISO 8601)")
# Переводим на 4 языка
logger.info(f"Translating feed post from {source_language} → PL/RU/UK/EN")
translations = await translate_to_all_languages(title, post_body, source_language)
# Fallback: если перевод не удался — копируем source для всех языков
if not translations:
logger.warning("Translation failed — falling back to source_language for all")
translations = {}
for lang in FEED_TARGET_LANGUAGES:
translations[f"title_{lang}"] = title
translations[f"body_{lang}"] = post_body
pool = await get_pool()
async with pool.acquire() as conn:
row = await conn.fetchrow("""
INSERT INTO feed_posts (
section, source_language,
title_pl, title_ru, title_uk, title_en,
body_pl, body_ru, body_uk, body_en,
link_url, telegram_url,
author_id, is_pinned, is_published, expires_at
) VALUES (
$1, $2,
$3, $4, $5, $6,
$7, $8, $9, $10,
$11, $12,
$13, $14, TRUE, $15
)
RETURNING id
""",
section, source_language,
translations["title_pl"], translations["title_ru"],
translations["title_uk"], translations["title_en"],
translations["body_pl"], translations["body_ru"],
translations["body_uk"], translations["body_en"],
link_url, telegram_url,
telegram_id, is_pinned, expires_at,
)
return {"status": "ok", "post_id": row["id"]}
@router.put("/admin/feed/post/{post_id}")
async def admin_update_feed_post(request: Request, post_id: int):
"""
Редактировать feed-пост.
Тело запроса (JSON) — все поля опциональны:
{
"title": "...", (на source_language)
"body": "...", (на source_language)
"link_url": "..." | null,
"telegram_url": "..." | null,
"is_pinned": true/false,
"is_published": true/false,
"expires_at": "..." | null,
"translate_again": true — перевести заново через AI
}
"""
telegram_id, _ = await require_auth(request)
_require_admin(telegram_id)
body = await request.json()
pool = await get_pool()
async with pool.acquire() as conn:
existing = await conn.fetchrow("""
SELECT * FROM feed_posts WHERE id = $1
""", post_id)
if not existing:
raise HTTPException(404, "Post not found")
source_language = existing["source_language"]
# Мерджим поля: если не передано — оставляем старое
title = body.get("title", existing.get(f"title_{source_language}") or "")
post_body = body.get("body", existing.get(f"body_{source_language}") or "")
link_url = body.get("link_url", existing["link_url"])
telegram_url = body.get("telegram_url", existing["telegram_url"])
is_pinned = body.get("is_pinned", existing["is_pinned"])
is_published = body.get("is_published", existing["is_published"])
# expires_at: явное None → очистить, отсутствие ключа → оставить
if "expires_at" in body:
expires_at_raw = body.get("expires_at")
if expires_at_raw is None:
expires_at = None
else:
try:
from datetime import datetime, timezone
normalized = str(expires_at_raw).replace("Z", "+00:00")
expires_at = datetime.fromisoformat(normalized)
if expires_at.tzinfo is None:
expires_at = expires_at.replace(tzinfo=timezone.utc)
except (ValueError, AttributeError):
raise HTTPException(400, "Invalid expires_at format (use ISO 8601)")
else:
expires_at = existing["expires_at"]
# Если translate_again = True — переводим заново
if body.get("translate_again"):
logger.info(f"Re-translating feed post #{post_id} from {source_language}")
translations = await translate_to_all_languages(title, post_body, source_language)
if not translations:
logger.warning("Translation failed — keeping old translations for other languages")
# Оставляем старые версии для не-source языков, обновляем только source
translations = {
"title_pl": existing["title_pl"], "title_ru": existing["title_ru"],
"title_uk": existing["title_uk"], "title_en": existing["title_en"],
"body_pl": existing["body_pl"], "body_ru": existing["body_ru"],
"body_uk": existing["body_uk"], "body_en": existing["body_en"],
}
translations[f"title_{source_language}"] = title
translations[f"body_{source_language}"] = post_body
else:
# Без translate_again — обновляем ТОЛЬКО source_language, остальные не трогаем
translations = {
"title_pl": existing["title_pl"], "title_ru": existing["title_ru"],
"title_uk": existing["title_uk"], "title_en": existing["title_en"],
"body_pl": existing["body_pl"], "body_ru": existing["body_ru"],
"body_uk": existing["body_uk"], "body_en": existing["body_en"],
}
translations[f"title_{source_language}"] = title
translations[f"body_{source_language}"] = post_body
await conn.execute("""
UPDATE feed_posts SET
title_pl = $1, title_ru = $2, title_uk = $3, title_en = $4,
body_pl = $5, body_ru = $6, body_uk = $7, body_en = $8,
link_url = $9, telegram_url = $10,
is_pinned = $11, is_published = $12,
expires_at = $13,
updated_at = NOW(),
edited_at = NOW()
WHERE id = $14
""",
translations["title_pl"], translations["title_ru"],
translations["title_uk"], translations["title_en"],
translations["body_pl"], translations["body_ru"],
translations["body_uk"], translations["body_en"],
link_url, telegram_url,
is_pinned, is_published,
expires_at,
post_id,
)
return {"status": "ok", "post_id": post_id}
# 🔧 ФИКС (18.09.2026): POST вместо DELETE.
# Причина: Telegram WebView (iOS / macOS) не пропускает DELETE из-за CORS preflight,
# который Cloudflare кеширует на 20 дней. POST без Content-Type — simple request,
# preflight не отправляется, Cloudflare не вмешивается.
#
# 🔧 ФИКС (19.09.2026): удаляем не только запись из БД, но и файлы с диска.
# Раньше файлы копились мёртвым грузом (27 мёртвых файлов за день тестов).
@router.post("/admin/feed/post/{post_id}/delete")
async def admin_delete_feed_post(request: Request, post_id: int):
"""
Удалить feed-пост (POST вместо DELETE — обход CORS preflight в WebView).
Что делаем:
1. Читаем пути всех файлов поста из feed_attachments ДО удаления из БД.
2. Удаляем пост из БД (ON DELETE CASCADE уберёт вложения).
3. Удаляем файлы с диска.
Если файл уже удалён вручную / нет прав — не роняем запрос, только логируем.
Пост из БД уже удалён — файл вторичен.
"""
telegram_id, _ = await require_auth(request)
_require_admin(telegram_id)
pool = await get_pool()
async with pool.acquire() as conn:
# 1. Читаем пути файлов ДО удаления
attachment_rows = await conn.fetch("""
SELECT file_path FROM feed_attachments WHERE post_id = $1
""", post_id)
# 2. Удаляем пост (CASCADE уберёт feed_attachments)
result = await conn.execute("""
DELETE FROM feed_posts WHERE id = $1
""", post_id)
affected = int(result.split()[-1])
if affected == 0:
raise HTTPException(404, "Post not found")
logger.info(f"Feed post #{post_id} deleted by admin {telegram_id}")
# 3. Удаляем файлы с диска (вне pool.acquire — чтобы не держать коннект)
# file_path в БД = "/uploads/feed/16_0_abc.jpg"
# на диске = "/root/filwordspl/backend/uploads/feed/16_0_abc.jpg"
# UPLOADS_DIR = "/root/filwordspl/backend/uploads" (Path)
removed_count = 0
failed_count = 0
for row in attachment_rows:
file_path = row["file_path"] # "/uploads/feed/16_0_abc.jpg"
# Убираем префикс "/uploads/" → "feed/16_0_abc.jpg"
rel_path = file_path.replace("/uploads/", "", 1).lstrip("/")
abs_path = Path(UPLOADS_DIR) / rel_path
try:
if abs_path.exists():
abs_path.unlink()
removed_count += 1
logger.info(f"[delete_feed_post] removed file: {abs_path}")
else:
logger.warning(f"[delete_feed_post] file not found: {abs_path}")
except Exception as e:
failed_count += 1
# Не роняем запрос — пост уже удалён из БД, файл вторичен
logger.error(f"[delete_feed_post] failed to remove {abs_path}: {e}")
if removed_count or failed_count:
logger.info(
f"[delete_feed_post] post #{post_id}: "
f"files removed={removed_count}, failed={failed_count}"
)
return {"status": "ok", "files_removed": removed_count}
@router.post("/admin/feed/upload-image")
async def admin_upload_feed_image(
request: Request,
post_id: int = Form(...),
file: UploadFile = File(...),
):
"""
Загрузить картинку к посту.
- Максимум MAX_FEED_ATTACHMENTS (3) на пост.
- Максимум MAX_FEED_IMAGE_SIZE_MB (5 МБ) на файл.
- Только image/jpeg, image/png, image/webp, image/gif.
Возвращает путь к файлу (используется как URL на фронте).
"""
telegram_id, _ = await require_auth(request)
_require_admin(telegram_id)
# Валидация MIME-типа
content_type = (file.content_type or "").lower()
if content_type not in FEED_ALLOWED_IMAGE_TYPES:
raise HTTPException(
400,
f"Invalid image type: {content_type}. Allowed: {sorted(FEED_ALLOWED_IMAGE_TYPES)}",
)
# Читаем файл, проверяем размер
content = await file.read()
if len(content) == 0:
raise HTTPException(400, "Empty file")
if len(content) > MAX_FEED_IMAGE_SIZE_BYTES:
raise HTTPException(
400,
f"File too large: {len(content)} bytes. Max: {MAX_FEED_IMAGE_SIZE_BYTES} bytes",
)
pool = await get_pool()
async with pool.acquire() as conn:
# Проверяем, что пост существует
post_exists = await conn.fetchval(
"SELECT 1 FROM feed_posts WHERE id = $1", post_id
)
if not post_exists:
raise HTTPException(404, "Post not found")
# Проверяем лимит вложений
current_count = await conn.fetchval("""
SELECT COUNT(*) FROM feed_attachments WHERE post_id = $1
""", post_id) or 0
if current_count >= MAX_FEED_ATTACHMENTS:
raise HTTPException(
400,
f"Max {MAX_FEED_ATTACHMENTS} attachments per post (already {current_count})",
)
# Готовим имя файла: {post_id}_{order}_{uuid8}.{ext}
ext_map = {
"image/jpeg": "jpg",
"image/jpg": "jpg",
"image/png": "png",
"image/webp": "webp",
"image/gif": "gif",
}
ext = ext_map.get(content_type, "jpg")
order_number = current_count # 0, 1, 2
filename = f"{post_id}_{order_number}_{uuid.uuid4().hex[:8]}.{ext}"
# Сохраняем на диск
feed_uploads_dir = Path(UPLOADS_DIR) / "feed"
feed_uploads_dir.mkdir(parents=True, exist_ok=True)
file_path_disk = feed_uploads_dir / filename
file_path_disk.write_bytes(content)
# Публичный URL для фронта (относительный — фронт сам подставит API_BASE)
public_path = f"/uploads/feed/{filename}"
# Пишем в БД
await conn.execute("""
INSERT INTO feed_attachments (post_id, file_path, file_type, order_number)
VALUES ($1, $2, $3, $4)
""", post_id, public_path, "image", order_number)
return {
"status": "ok",
"file_path": public_path,
"order_number": order_number,
}
@router.post("/admin/feed/translate")
async def admin_translate_feed(request: Request):
"""
Перевести title+body БЕЗ сохранения в БД.
Используется для превью перевода в админ-UI.
Тело запроса:
{
"title": "...",
"body": "...",
"source_language": "ru"
}
Возвращает:
{
"title_pl": "...", "title_ru": "...", "title_uk": "...", "title_en": "...",
"body_pl": "...", "body_ru": "...", "body_uk": "...", "body_en": "..."
}
"""
telegram_id, _ = await require_auth(request)
_require_admin(telegram_id)
body = await request.json()
title = (body.get("title") or "").strip()
post_body = (body.get("body") or "").strip()
source_language = body.get("source_language", "ru").strip().lower()
if source_language not in FEED_TARGET_LANGUAGES:
raise HTTPException(400, f"Invalid source_language. Allowed: {FEED_TARGET_LANGUAGES}")
if not title and not post_body:
raise HTTPException(400, "Either title or body is required")
translations = await translate_to_all_languages(title, post_body, source_language)
if not translations:
raise HTTPException(500, "Translation failed (AI unavailable or invalid response)")
return translations
+330
View File
@@ -0,0 +1,330 @@
import json
from fastapi import APIRouter, Request, HTTPException
from auth import require_auth
from database import get_pool
router = APIRouter(prefix="/game", tags=["game"])
@router.get("/progress")
async def get_progress(request: Request):
telegram_id, _ = await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
user = await conn.fetchrow(
"SELECT last_completed_level, current_level FROM users WHERE telegram_id = $1",
telegram_id
)
if not user:
return {
"last_completed_level": 0,
"total_score": 0,
"current_level": "A1"
}
current_level = user["current_level"] or "A1"
# Очки из филвордов
wordsearch_score = await conn.fetchval(
"SELECT COALESCE(SUM(score), 0) FROM user_progress WHERE user_id = $1",
telegram_id
)
# Очки из предложений
sentence_score = await conn.fetchval(
"SELECT COALESCE(SUM(score), 0) FROM sentence_progress WHERE user_id = $1",
telegram_id
)
# Очки из квиза
wordquiz_score = await conn.fetchval(
"SELECT COALESCE(total_score, 0) FROM word_quiz_progress WHERE user_id = $1",
telegram_id
)
# Очки из Bubble Words
bubblewords_score = await conn.fetchval(
"SELECT COALESCE(total_score, 0) FROM bubble_words_progress WHERE user_id = $1",
telegram_id
)
total_score = (wordsearch_score or 0) + (sentence_score or 0) + (wordquiz_score or 0) + (bubblewords_score or 0)
return {
"last_completed_level": user["last_completed_level"] or 0,
"total_score": total_score,
"current_level": current_level
}
@router.get("/level/{level_number}")
async def get_level(request: Request, level_number: int):
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, last_completed_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"
last_completed = user["last_completed_level"] or 0
# Проверяем, что уровень доступен (не дальше следующего за пройденным)
if level_number > 1 and level_number > last_completed + 1:
raise HTTPException(403, "Level not available yet. Complete previous level first.")
# Получаем уровень по его номеру ВНУТРИ выбранного уровня
level = await conn.fetchrow(
"SELECT * FROM levels WHERE level = $1 AND level_number = $2", current_level, level_number
)
if not level:
raise HTTPException(404, "Level not found")
words = await conn.fetch(
"SELECT * FROM level_words WHERE level_id = $1", level["id"]
)
progress = await conn.fetchrow(
"SELECT * FROM user_progress WHERE user_id = $1 AND level_id = $2",
telegram_id, level["id"],
)
word_list = []
for w in words:
translation = w.get(f"translation_{lang}", "") or w.get("translation_pl", "") or w.get("translation_en", "")
word_list.append({
"word_pl": w["word_pl"],
"translation": translation,
})
grid_letters = level["grid_letters"]
if isinstance(grid_letters, str):
grid_letters = json.loads(grid_letters)
word_paths = level["word_paths"]
if isinstance(word_paths, str):
word_paths = json.loads(word_paths)
return {
"level": {
"id": level["id"],
"level_number": level["level_number"],
"grid_size": level["grid_size"],
"grid_letters": grid_letters,
"word_paths": word_paths,
"level": level["level"],
},
"words": word_list,
"progress": {
"completed": progress is not None,
"hints_used": progress["hints_used"] if progress else 0,
"score": progress["score"] if progress else 0,
},
}
@router.post("/check-word")
async def check_word(request: Request):
telegram_id, _ = await require_auth(request)
body = await request.json()
level_id = body.get("level_id")
selected_cells = body.get("cells", [])
if not level_id or not selected_cells:
raise HTTPException(400, "level_id and cells required")
pool = await get_pool()
found_word = None
perfect_count = body.get("perfect_streak", 0)
async with pool.acquire() as conn:
level = await conn.fetchrow("SELECT * FROM levels WHERE id = $1", level_id)
if not level:
raise HTTPException(404, "Level not found")
word_paths = level["word_paths"]
if isinstance(word_paths, str):
word_paths = json.loads(word_paths)
grid_letters = level["grid_letters"]
if isinstance(grid_letters, str):
grid_letters = json.loads(grid_letters)
selected_word = ''
for cell in selected_cells:
r, c = cell[0], cell[1]
try:
selected_word += grid_letters[r][c]
except (IndexError, TypeError):
continue
selected_word = selected_word.lower()
for word in word_paths.keys():
if word.lower() == selected_word:
found_word = word
break
# 🔧 ИСПРАВЛЕНО: вызов достижений ВНЕ блока async with pool.acquire()
if found_word:
from routers.achievements import check_and_award_achievements
new_achievements = []
if perfect_count > 0:
ach = await check_and_award_achievements(telegram_id, "perfect_words", perfect_count)
new_achievements.extend(ach)
return {
"status": "ok",
"found": True,
"word": found_word,
"new_achievements": new_achievements
}
return {"status": "ok", "found": False}
@router.post("/complete-level")
async def complete_level(request: Request):
"""Завершение уровня с проверкой на читерство и выдачей достижений"""
telegram_id, _ = await require_auth(request)
body = await request.json()
level_id = body.get("level_id")
hints_used = body.get("hints_used", 0)
completion_time = body.get("completion_time")
perfect_streak = body.get("perfect_streak", 0)
if not level_id:
raise HTTPException(400, "level_id required")
hints_used = min(max(0, hints_used), 3)
# Защита от читерства: минимальное время 5 секунд
if completion_time is not None and completion_time < 5:
raise HTTPException(400, "Completion time too fast. Minimum 5 seconds required.")
pool = await get_pool()
# 🔧 ИСПРАВЛЕНО: все проверки и запись прогресса — внутри одного блока
# все данные для достижений собираем здесь, а сами вызовы делаем снаружи
already_completed = False
existing_score = 0
score = 0
levels_completed = 0
grand_total = 0
async with pool.acquire() as conn:
user = await conn.fetchrow(
"SELECT id, telegram_id, last_completed_level, current_level FROM users WHERE telegram_id = $1",
telegram_id
)
if not user:
raise HTTPException(401, "User not registered")
level = await conn.fetchrow(
"SELECT id, level_number, grid_size, level FROM levels WHERE id = $1", level_id
)
if not level:
raise HTTPException(404, "Level not found")
current_level_number = level["level_number"]
level_name = level["level"] or "A1"
# Проверяем, что уровень принадлежит текущему уровню пользователя
if level_name != (user["current_level"] or "A1"):
raise HTTPException(403, "This level does not belong to your current level")
existing_progress = await conn.fetchrow(
"SELECT score FROM user_progress WHERE user_id = $1 AND level_id = $2",
telegram_id, level_id
)
if existing_progress:
already_completed = True
existing_score = existing_progress["score"]
else:
score = max(10, 100 - hints_used * 10)
await conn.execute(
"""INSERT INTO user_progress (user_id, level_id, completed_at, hints_used, score, level)
VALUES ($1, $2, NOW(), $3, $4, $5)""",
telegram_id, level_id, hints_used, score, level_name,
)
# Обновляем last_completed_level (максимальный номер пройденного уровня)
await conn.execute(
"""UPDATE users
SET last_completed_level = GREATEST(last_completed_level, $2)
WHERE telegram_id = $1""",
telegram_id, current_level_number,
)
# Собираем данные для достижений внутри блока
levels_completed = await conn.fetchval(
"SELECT COUNT(*) FROM user_progress WHERE user_id = $1",
telegram_id
) or 0
total_score = await conn.fetchval(
"SELECT COALESCE(SUM(score), 0) FROM user_progress WHERE user_id = $1",
telegram_id
) or 0
sentence_score = await conn.fetchval(
"SELECT COALESCE(SUM(score), 0) FROM sentence_progress WHERE user_id = $1",
telegram_id
) or 0
wordquiz_score = await conn.fetchval(
"SELECT COALESCE(total_score, 0) FROM word_quiz_progress WHERE user_id = $1",
telegram_id
) or 0
bubblewords_score = await conn.fetchval(
"SELECT COALESCE(total_score, 0) FROM bubble_words_progress WHERE user_id = $1",
telegram_id
) or 0
grand_total = total_score + sentence_score + wordquiz_score + bubblewords_score
# Если уровень уже был пройден — просто возвращаем результат
if already_completed:
return {
"status": "ok",
"score": existing_score,
"already_completed": True,
"new_achievements": []
}
# 🔧 ИСПРАВЛЕНО: вызовы достижений ВНЕ блока async with pool.acquire()
from routers.achievements import check_and_award_achievements
new_achievements = []
ach = await check_and_award_achievements(telegram_id, "levels_completed", levels_completed)
new_achievements.extend(ach)
if hints_used == 0:
ach = await check_and_award_achievements(telegram_id, "no_hints_level", 1)
new_achievements.extend(ach)
if completion_time is not None and completion_time > 0:
if int(completion_time) <= 60:
ach = await check_and_award_achievements(telegram_id, "speed_level", 60)
new_achievements.extend(ach)
ach = await check_and_award_achievements(telegram_id, "total_score", grand_total)
new_achievements.extend(ach)
if perfect_streak >= 5:
ach = await check_and_award_achievements(telegram_id, "perfect_words", perfect_streak)
new_achievements.extend(ach)
return {
"status": "ok",
"score": score,
"already_completed": False,
"new_achievements": new_achievements
}
+152
View File
@@ -0,0 +1,152 @@
from fastapi import APIRouter, Request, HTTPException
from auth import require_auth
from database import get_pool
router = APIRouter(prefix="/hints", tags=["hints"])
@router.get("/first-letter/{level_id}")
async def get_first_letter_hint(request: Request, level_id: int):
"""Подсказка: показывает первую букву случайного ненайденного слова"""
telegram_id, _ = await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
# Проверяем, что уровень существует
level = await conn.fetchrow("SELECT * FROM levels WHERE id = $1", level_id)
if not level:
raise HTTPException(404, "Level not found")
# Проверяем, что пользователь существует
user_exists = await conn.fetchval(
"SELECT 1 FROM users WHERE telegram_id = $1", telegram_id
)
if not user_exists:
raise HTTPException(401, "User not registered")
# Получаем прогресс пользователя по этому уровню
progress = await conn.fetchrow(
"SELECT hints_used FROM user_progress WHERE user_id = $1 AND level_id = $2",
telegram_id, level_id
)
hints_already_used = progress["hints_used"] if progress else 0
# Получаем все слова уровня
words = await conn.fetch(
"SELECT word_pl FROM level_words WHERE level_id = $1", level_id
)
if not words:
raise HTTPException(404, "No words found for this level")
word_paths = level["word_paths"]
if isinstance(word_paths, str):
import json
word_paths = json.loads(word_paths)
# Находим первое слово, у которого есть путь
all_level_words = [w["word_pl"] for w in words]
# Ищем слово с путём (сначала основные, потом filler-слова)
found_word = None
first_cell = None
for word in all_level_words:
if word in word_paths and word_paths[word]:
found_word = word
first_cell = word_paths[word][0]
break
if not found_word:
# Если пути не нашлись — берём любое слово
found_word = all_level_words[0]
first_cell = [0, 0]
return {
"status": "ok",
"word": found_word,
"first_cell": first_cell,
"hints_used_total": hints_already_used,
}
@router.get("/word-list/{level_id}")
async def get_word_list(request: Request, level_id: int):
"""Подсказка: показывает список всех слов уровня с переводами"""
telegram_id, _ = await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
# Проверяем, что уровень существует
level = await conn.fetchrow("SELECT id FROM levels WHERE id = $1", level_id)
if not level:
raise HTTPException(404, "Level not found")
# Проверяем, что пользователь существует
user_exists = await conn.fetchval(
"SELECT 1 FROM users WHERE telegram_id = $1", telegram_id
)
if not user_exists:
raise HTTPException(401, "User not registered")
# Получаем прогресс пользователя
progress = await conn.fetchrow(
"SELECT hints_used FROM user_progress WHERE user_id = $1 AND level_id = $2",
telegram_id, level_id
)
hints_already_used = progress["hints_used"] if progress else 0
# Получаем слова
words = await conn.fetch(
"SELECT * FROM level_words WHERE level_id = $1 ORDER BY word_pl", level_id
)
# Определяем язык пользователя
user = await conn.fetchrow(
"SELECT app_language FROM users WHERE telegram_id = $1", telegram_id
)
lang = user["app_language"] if user else "pl"
# Формируем список с переводами
translation_field = f"translation_{lang}"
word_list = []
for w in words:
translation = (
w.get(translation_field) or
w.get("translation_pl") or
w.get("translation_en") or
""
)
word_list.append({
"word_pl": w["word_pl"],
"translation": translation,
})
return {
"status": "ok",
"words": word_list,
"total_words": len(word_list),
"hints_used_total": hints_already_used,
}
@router.get("/remaining/{level_id}")
async def get_remaining_hints(request: Request, level_id: int):
"""Возвращает количество оставшихся подсказок для уровня"""
telegram_id, _ = await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
progress = await conn.fetchrow(
"SELECT hints_used FROM user_progress WHERE user_id = $1 AND level_id = $2",
telegram_id, level_id
)
hints_used = progress["hints_used"] if progress else 0
max_hints = 3
remaining = max(0, max_hints - hints_used)
return {
"max_hints": max_hints,
"hints_used": hints_used,
"remaining": remaining,
}
+333
View File
@@ -0,0 +1,333 @@
from fastapi import APIRouter, Request, HTTPException
from auth import require_auth
from database import get_pool
from config import ADMIN_IDS
router = APIRouter(prefix="/leaderboard", tags=["leaderboard"])
LEVELS = ["A1", "A2", "B1", "B2", "C1", "C2"]
@router.get("")
async def get_leaderboard(request: Request, limit: int = 50):
"""Рейтинг игроков — быстрое чтение из кэша"""
telegram_id, _ = await require_auth(request)
if limit < 1:
limit = 1
elif limit > 100:
limit = 100
pool = await get_pool()
async with pool.acquire() as conn:
user = await conn.fetchrow(
"SELECT 1, current_level FROM users WHERE telegram_id = $1", telegram_id
)
if not user:
raise HTTPException(401, "User not registered")
current_level = user["current_level"] or "A1"
# Быстрое чтение из MATERIALIZED VIEW
rows = await conn.fetch("""
SELECT * FROM leaderboard_cache
ORDER BY total_score DESC
LIMIT $1
""", limit)
leaderboard = []
for i, row in enumerate(rows):
leaderboard.append({
"rank": i + 1,
"telegram_id": row["telegram_id"],
"username": row["username"] or f"Player{row['telegram_id']}",
"total_score": row["total_score"],
"wordsearch_score": row["wordsearch_score"],
"sentence_score": row["sentence_score"],
"wordquiz_score": row["wordquiz_score"],
"words_guessed": row["words_guessed"],
"bubblewords_score": row["bubblewords_score"],
"bubble_levels_completed": row["bubble_levels_completed"],
"levels_completed": 0,
"sentences_completed": 0,
})
# Место пользователя
user_rank = await conn.fetchval("""
SELECT COUNT(*) + 1 FROM leaderboard_cache
WHERE total_score > (
SELECT COALESCE(total_score, 0) FROM leaderboard_cache WHERE telegram_id = $1
)
""", telegram_id)
# Персональная статистика пользователя
my_row = await conn.fetchrow("""
SELECT * FROM leaderboard_cache WHERE telegram_id = $1
""", telegram_id)
if my_row:
my_wordsearch = my_row["wordsearch_score"]
my_sentence = my_row["sentence_score"]
my_wordquiz = my_row["wordquiz_score"]
my_words_guessed = my_row["words_guessed"]
my_bubblewords = my_row["bubblewords_score"]
my_bubble_levels = my_row["bubble_levels_completed"]
else:
my_wordsearch = 0
my_sentence = 0
my_wordquiz = 0
my_words_guessed = 0
my_bubblewords = 0
my_bubble_levels = 0
# Считаем НАПРЯМУЮ из основных таблиц
my_wordsearch_levels = await conn.fetchval(
"SELECT COUNT(DISTINCT level_id) FROM user_progress WHERE user_id = $1 AND completed_at IS NOT NULL",
telegram_id
) or 0
my_sentences_completed = await conn.fetchval(
"SELECT COUNT(*) FROM sentence_progress WHERE user_id = $1 AND completed_at IS NOT NULL",
telegram_id
) or 0
# Если пользователя нет в кэше — считаем очки из основных таблиц
if not my_row:
my_wordsearch = await conn.fetchval(
"SELECT COALESCE(SUM(score), 0) FROM user_progress WHERE user_id = $1 AND completed_at IS NOT NULL",
telegram_id
) or 0
my_sentence = await conn.fetchval(
"SELECT COALESCE(SUM(score), 0) FROM sentence_progress WHERE user_id = $1 AND completed_at IS NOT NULL",
telegram_id
) or 0
my_wordquiz = await conn.fetchval(
"SELECT COALESCE(total_score, 0) FROM word_quiz_progress WHERE user_id = $1",
telegram_id
) or 0
my_words_guessed = await conn.fetchval(
"SELECT COALESCE(words_guessed, 0) FROM word_quiz_progress WHERE user_id = $1",
telegram_id
) or 0
my_bubblewords = await conn.fetchval(
"SELECT COALESCE(total_score, 0) FROM bubble_words_progress WHERE user_id = $1",
telegram_id
) or 0
my_bubble_levels = await conn.fetchval(
"SELECT COALESCE(levels_completed, 0) FROM bubble_words_progress WHERE user_id = $1",
telegram_id
) or 0
my_total = my_wordsearch + my_sentence + my_wordquiz + my_bubblewords
my_position = user_rank or 1
# 🔧 ФИКС N+1 (19.09.2026): 48 запросов → 5.
# Было: цикл по 6 уровням × 8 запросов = 48 round-trip к БД.
# Стало: 5 агрегированных запросов + сборка level_stats в Python.
# 1. Справочники (не зависят от юзера) — одним запросом
ref_row = await conn.fetchrow("""
SELECT
(SELECT COUNT(*) FROM levels WHERE level = 'A1') AS a1_levels,
(SELECT COUNT(*) FROM levels WHERE level = 'A2') AS a2_levels,
(SELECT COUNT(*) FROM levels WHERE level = 'B1') AS b1_levels,
(SELECT COUNT(*) FROM levels WHERE level = 'B2') AS b2_levels,
(SELECT COUNT(*) FROM levels WHERE level = 'C1') AS c1_levels,
(SELECT COUNT(*) FROM levels WHERE level = 'C2') AS c2_levels,
(SELECT COUNT(*) FROM sentences WHERE level = 'A1') AS a1_sent,
(SELECT COUNT(*) FROM sentences WHERE level = 'A2') AS a2_sent,
(SELECT COUNT(*) FROM sentences WHERE level = 'B1') AS b1_sent,
(SELECT COUNT(*) FROM sentences WHERE level = 'B2') AS b2_sent,
(SELECT COUNT(*) FROM sentences WHERE level = 'C1') AS c1_sent,
(SELECT COUNT(*) FROM sentences WHERE level = 'C2') AS c2_sent
""")
# 2. Очки и прогресс по всем уровням сразу — 4 запроса с GROUP BY
ws_rows = await conn.fetch("""
SELECT level,
COALESCE(SUM(score), 0) AS score,
COUNT(*) AS completed
FROM user_progress
WHERE user_id = $1 AND completed_at IS NOT NULL
GROUP BY level
""", telegram_id)
ws_by_level = {r["level"]: r for r in ws_rows}
s_rows = await conn.fetch("""
SELECT level,
COALESCE(SUM(score), 0) AS score,
COUNT(*) AS completed
FROM sentence_progress
WHERE user_id = $1 AND completed_at IS NOT NULL
GROUP BY level
""", telegram_id)
s_by_level = {r["level"]: r for r in s_rows}
wq_rows = await conn.fetch("""
SELECT level, COALESCE(total_score, 0) AS score
FROM word_quiz_progress
WHERE user_id = $1
""", telegram_id)
wq_by_level = {r["level"]: r for r in wq_rows}
bw_rows = await conn.fetch("""
SELECT level, COALESCE(total_score, 0) AS score
FROM bubble_words_progress
WHERE user_id = $1
""", telegram_id)
bw_by_level = {r["level"]: r for r in bw_rows}
# 3. Собираем level_stats в Python — без единого запроса
level_stats = {}
for lvl in LEVELS:
lvl_lower = lvl.lower()
ws_score = ws_by_level.get(lvl, {}).get("score", 0) or 0
ws_completed = ws_by_level.get(lvl, {}).get("completed", 0) or 0
s_score = s_by_level.get(lvl, {}).get("score", 0) or 0
s_completed = s_by_level.get(lvl, {}).get("completed", 0) or 0
wq_score = wq_by_level.get(lvl, {}).get("score", 0) or 0
bw_score = bw_by_level.get(lvl, {}).get("score", 0) or 0
total_ws_levels = ref_row[f"{lvl_lower}_levels"] or 0
total_sentences = ref_row[f"{lvl_lower}_sent"] or 0
total_items = total_ws_levels + total_sentences
completed_items = ws_completed + s_completed
progress_percent = round((completed_items / total_items * 100), 1) if total_items > 0 else 0
level_stats[lvl] = {
"total_score": ws_score + s_score + wq_score + bw_score,
"progress_percent": progress_percent,
"wordsearch_score": ws_score,
"sentence_score": s_score,
"wordquiz_score": wq_score,
"bubblewords_score": bw_score,
}
# 🔧 ИСПРАВЛЕНО: вызов достижений ВНЕ блока async with pool.acquire()
from routers.achievements import check_and_award_achievements
if my_position <= 3:
await check_and_award_achievements(telegram_id, "rank_reached", my_position)
return {
"leaderboard": leaderboard,
"current_level": current_level,
"my_rank": {
"rank": my_position,
"telegram_id": telegram_id,
"total_score": my_total,
"wordsearch_score": my_wordsearch,
"wordsearch_levels": my_wordsearch_levels,
"sentence_score": my_sentence,
"sentences_completed": my_sentences_completed,
"wordquiz_score": my_wordquiz,
"words_guessed": my_words_guessed,
"bubblewords_score": my_bubblewords,
"bubble_levels_completed": my_bubble_levels,
},
"level_stats": level_stats,
}
@router.get("/admin/stats")
async def get_admin_stats(request: Request):
"""Админ-статистика: только для ADMIN_IDS"""
telegram_id, _ = await require_auth(request)
if telegram_id not in ADMIN_IDS:
raise HTTPException(403, "Access denied")
pool = await get_pool()
async with pool.acquire() as conn:
total_users = await conn.fetchval("SELECT COUNT(*) FROM users") or 0
new_today = await conn.fetchval(
"SELECT COUNT(*) FROM users WHERE created_at::date = CURRENT_DATE"
) or 0
online_now = await conn.fetchval("""
SELECT COUNT(DISTINCT user_id) FROM (
SELECT user_id FROM user_progress WHERE completed_at > NOW() - INTERVAL '30 minutes'
UNION
SELECT user_id FROM sentence_progress WHERE completed_at > NOW() - INTERVAL '30 minutes'
UNION
SELECT user_id FROM daily_logins WHERE login_date = CURRENT_DATE
) active
""") or 0
ws_players = await conn.fetchval(
"SELECT COUNT(DISTINCT user_id) FROM user_progress WHERE completed_at IS NOT NULL"
) or 0
s_players = await conn.fetchval(
"SELECT COUNT(DISTINCT user_id) FROM sentence_progress WHERE completed_at IS NOT NULL"
) or 0
wq_players = await conn.fetchval(
"SELECT COUNT(*) FROM word_quiz_progress WHERE words_guessed > 0"
) or 0
bw_players = await conn.fetchval(
"SELECT COUNT(*) FROM bubble_words_progress WHERE levels_completed > 0"
) or 0
# 🔧 ИСПРАВЛЕНО: 'open_flashcards' → 'open_teacher_menu'
# Раньше метрика всегда была 0, потому что такого кода достижения больше нет
teacher_menu_opened = await conn.fetchval("""
SELECT COUNT(*) FROM user_achievements ua
JOIN achievements a ON ua.achievement_id = a.id
WHERE a.code = 'open_teacher_menu'
""") or 0
# Количество пользователей по уровням
level_counts = {}
for lvl in LEVELS:
count = await conn.fetchval(
"SELECT COUNT(*) FROM users WHERE current_level = $1", lvl
) or 0
level_counts[lvl] = count
return {
"total_users": total_users,
"new_today": new_today,
"online_now": online_now,
"wordsearch_players": ws_players,
"sentence_players": s_players,
"wordquiz_players": wq_players,
"bubblewords_players": bw_players,
"teacher_menu_opened": teacher_menu_opened,
"level_counts": level_counts,
}
@router.get("/top")
async def get_top_players(request: Request):
"""Топ-3 игрока для главного экрана — из кэша"""
telegram_id, _ = await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
rows = await conn.fetch("""
SELECT telegram_id, username, total_score
FROM leaderboard_cache
ORDER BY total_score DESC
LIMIT 3
""")
top3 = []
medals = ["🥇", "🥈", "🥉"]
for i, row in enumerate(rows):
top3.append({
"rank": i + 1,
"medal": medals[i],
"username": row["username"] or f"Player{row['telegram_id']}",
"total_score": row["total_score"],
})
return {"top3": top3}
+872
View File
@@ -0,0 +1,872 @@
"""
Learning Router — система уроков Filwords PL.
Уровни → Темы → Уроки → Финальный тест → Аналитика.
Данные в отдельных файлах: learning_data/level_*.py
"""
import logging
import json
import statistics
from datetime import datetime, timezone
import httpx
import re
from fastapi import APIRouter, Request, HTTPException
from auth import require_auth
from database import get_pool
from config import TOGETHER_API_KEY
from .learning_data import init_learning_data
from .learning_profile import get_weak_skills, get_strong_skills
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/learning", tags=["learning"])
TOGETHER_API_URL = "https://api.together.xyz/v1/chat/completions"
ANALYTICS_MODEL = "MiniMaxAI/MiniMax-M3"
# Доступные функции приложения (для AI — чтобы не галлюцинировал)
AVAILABLE_FEATURES = [
"lessons",
"grammar_rules",
"cheat_sheets",
"word_game",
"sentence_game",
"choose_word",
"word_bubbles",
"pronunciation",
"ai_chat",
]
# Понятные названия разделов для пользователя
AVAILABLE_FEATURES_DESCRIPTION = """
Доступные разделы приложения (называй их ТАК, как они называются в интерфейсе пользователя):
- "Уроки" — уроки и тесты
- "Филворды" — игра в слова (найди слова в сетке букв)
- "Составь предложение" — составление предложений из слов
- "Выбери слово" — выбор правильного перевода
- "Пузыри слов" — сбор слов из пузырей
- "Произношение" — практика произношения
- "Грамматика" — правила и шпаргалки
- "AI-помощник" — чат с ИИ
НИКОГДА не используй технические названия: word_game, sentence_game, choose_word, word_bubbles, ai_chat, lessons, pronunciation, grammar_rules, cheat_sheets.
"""
async def get_skill_names(conn):
"""Загружает понятные названия навыков из БД (title_ru уроков)."""
rows = await conn.fetch("""
SELECT DISTINCT ON (skill_tag) skill_tag, title_ru
FROM learning_lessons
WHERE is_active = TRUE
ORDER BY skill_tag, order_number
""")
return {r["skill_tag"]: r["title_ru"] for r in rows}
async def get_weak_lessons(conn, skill_tags):
"""Находит уроки для перепрохождения по слабым навыкам."""
rows = await conn.fetch("""
SELECT DISTINCT ON (l.skill_tag) l.lesson_id, l.skill_tag, l.title_ru, t.name_ru as topic_name
FROM learning_lessons l
JOIN learning_topics t ON l.topic_id = t.topic_id
WHERE l.skill_tag = ANY($1) AND l.is_active = TRUE
ORDER BY l.skill_tag, l.order_number
""", skill_tags)
return [dict(r) for r in rows]
async def update_skill_mastery(conn, user_id: int, skill_tag: str):
"""Рассчитывает и обновляет мастерство по навыку на основе всех ответов пользователя"""
# Все ответы по навыку с question_type и wrong_because
answers = await conn.fetch("""
SELECT correct, time_taken_ms, hint_used, question_type, wrong_because, is_final_answer, created_at
FROM user_learning_answers
WHERE user_id = $1 AND skill_tag = $2
ORDER BY created_at ASC
""", user_id, skill_tag)
if not answers:
return None
total_attempts = len(answers)
correct_count = sum(1 for a in answers if a["correct"])
accuracy = (correct_count / total_attempts * 100) if total_attempts > 0 else 0
# Последние попытки
recent = answers[-5:]
recent_correct = sum(1 for a in recent if a["correct"])
recent_accuracy = (recent_correct / len(recent) * 100) if recent else 0
# last_3 / last_5 / last_10 accuracy
def calc_last_n(n):
last_n = answers[-n:] if len(answers) >= n else None
if not last_n or len(last_n) < n:
return None
correct = sum(1 for a in last_n if a["correct"])
return round((correct / n * 100), 2)
last_3_accuracy = calc_last_n(3)
last_5_accuracy = calc_last_n(5)
last_10_accuracy = calc_last_n(10)
# Время: среднее, медиана
all_times = [a["time_taken_ms"] for a in answers if a["time_taken_ms"] and a["time_taken_ms"] > 0]
correct_times = [a["time_taken_ms"] for a in answers if a["correct"] and a["time_taken_ms"] and a["time_taken_ms"] > 0]
wrong_times = [a["time_taken_ms"] for a in answers if not a["correct"] and a["time_taken_ms"] and a["time_taken_ms"] > 0]
avg_time_ms = int(sum(correct_times) / len(correct_times)) if correct_times else 0
median_time_ms = int(statistics.median(all_times)) if all_times else 0
median_correct_time_ms = int(statistics.median(correct_times)) if correct_times else 0
median_wrong_time_ms = int(statistics.median(wrong_times)) if wrong_times else 0
# Зависимость от подсказок
hints_used = sum(1 for a in answers if a["hint_used"])
hint_dependency = (hints_used / total_attempts * 100) if total_attempts > 0 else 0
# Разделение recognition / production / application
recognition_answers = [a for a in answers if a["question_type"] == "recognition"]
production_answers = [a for a in answers if a["question_type"] == "production"]
application_answers = [a for a in answers if a["question_type"] == "application"]
recognition_attempts = len(recognition_answers)
recognition_correct = sum(1 for a in recognition_answers if a["correct"])
production_attempts = len(production_answers)
production_correct = sum(1 for a in production_answers if a["correct"])
application_attempts = len(application_answers)
application_correct = sum(1 for a in application_answers if a["correct"])
recognition_score = round((recognition_correct / recognition_attempts * 100), 2) if recognition_attempts > 0 else None
production_score = round((production_correct / production_attempts * 100), 2) if production_attempts > 0 else None
application_score = round((application_correct / application_attempts * 100), 2) if application_attempts > 0 else None
# Passive / Active knowledge
passive_knowledge = recognition_score if recognition_score is not None else 0
active_scores = [s for s in [production_score, application_score] if s is not None]
active_knowledge = round(sum(active_scores) / len(active_scores), 2) if active_scores else 0
# Ошибки
error_count = sum(1 for a in answers if not a["correct"] and a["wrong_because"])
error_types = {}
for a in answers:
if not a["correct"] and a["wrong_because"]:
error_types[a["wrong_because"]] = error_types.get(a["wrong_because"], 0) + 1
repeated_error_count = sum(1 for count in error_types.values() if count >= 2)
# Дней с последней попытки
last_attempt = answers[-1]["created_at"]
if last_attempt:
now = datetime.now(timezone.utc)
if last_attempt.tzinfo is None:
last_attempt = last_attempt.replace(tzinfo=timezone.utc)
days_since = (now - last_attempt).days
else:
days_since = 0
# Мастерство
no_hint_score = max(0, 100 - hint_dependency)
mastery = round(accuracy * 0.4 + recent_accuracy * 0.3 + no_hint_score * 0.3, 2)
# Получаем прошлый mastery для расчёта delta
previous_row = await conn.fetchrow("""
SELECT mastery_score, previous_mastery_score FROM user_skill_mastery
WHERE user_id = $1 AND skill_tag = $2
""", user_id, skill_tag)
previous_mastery = float(previous_row["mastery_score"]) if previous_row and previous_row["mastery_score"] else mastery
mastery_delta = round(mastery - previous_mastery, 2)
# Trend
if total_attempts < 3:
trend = "insufficient_data"
elif last_3_accuracy is not None and last_10_accuracy is not None:
if last_3_accuracy > last_10_accuracy + 5:
trend = "improving"
elif last_3_accuracy < last_10_accuracy - 5:
trend = "declining"
else:
trend = "stable"
elif last_3_accuracy is not None and accuracy is not None:
if last_3_accuracy > accuracy + 5:
trend = "improving"
elif last_3_accuracy < accuracy - 5:
trend = "declining"
else:
trend = "stable"
else:
trend = "stable"
# Retention status
if days_since == 0:
retention_status = "fresh"
elif days_since <= 3:
retention_status = "fresh"
elif days_since <= 7:
retention_status = "aging"
elif days_since <= 14:
retention_status = "needs_review"
else:
retention_status = "overdue"
# Fluency score: скорость + точность + автоматизация
speed_score = 100 if median_correct_time_ms and median_correct_time_ms < 5000 else (50 if median_correct_time_ms and median_correct_time_ms < 15000 else 20)
fluency = round(accuracy * 0.4 + active_knowledge * 0.3 + speed_score * 0.3, 2)
# Уверенность
if len(recent) >= 3:
if all(a["correct"] for a in recent[-3:]):
confidence = min(100, mastery + 15)
elif not any(a["correct"] for a in recent[-3:]):
confidence = max(0, mastery - 20)
else:
confidence = mastery
else:
confidence = mastery
confidence = round(min(100, max(0, confidence)), 2)
# Сохраняем в user_skill_mastery
await conn.execute("""
INSERT INTO user_skill_mastery (
user_id, skill_tag, attempts_count, correct_count,
recognition_attempts, recognition_correct, recognition_score,
production_attempts, production_correct, production_score,
application_attempts, application_correct, application_score,
retention_score, hint_dependency, average_time_ms, median_time_ms,
median_correct_time_ms, median_wrong_time_ms,
recent_accuracy, last_3_accuracy, last_5_accuracy, last_10_accuracy,
long_term_accuracy, mastery_score, previous_mastery_score, mastery_delta,
trend, retention_status, fluency_score, passive_knowledge_score, active_knowledge_score,
confidence, error_count, repeated_error_count, days_since_last_attempt, last_attempt_at
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, 0, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33, $34, $35, NOW())
ON CONFLICT (user_id, skill_tag) DO UPDATE SET
attempts_count = $3,
correct_count = $4,
recognition_attempts = $5,
recognition_correct = $6,
recognition_score = $7,
production_attempts = $8,
production_correct = $9,
production_score = $10,
application_attempts = $11,
application_correct = $12,
application_score = $13,
hint_dependency = $14,
average_time_ms = $15,
median_time_ms = $16,
median_correct_time_ms = $17,
median_wrong_time_ms = $18,
recent_accuracy = $19,
last_3_accuracy = $20,
last_5_accuracy = $21,
last_10_accuracy = $22,
long_term_accuracy = $23,
mastery_score = $24,
previous_mastery_score = $25,
mastery_delta = $26,
trend = $27,
retention_status = $28,
fluency_score = $29,
passive_knowledge_score = $30,
active_knowledge_score = $31,
confidence = $32,
error_count = $33,
repeated_error_count = $34,
days_since_last_attempt = $35,
last_attempt_at = NOW()
""", user_id, skill_tag, total_attempts, correct_count, recognition_attempts, recognition_correct, recognition_score, production_attempts, production_correct, production_score, application_attempts, application_correct, application_score, hint_dependency, avg_time_ms, median_time_ms, median_correct_time_ms, median_wrong_time_ms, recent_accuracy, last_3_accuracy, last_5_accuracy, last_10_accuracy, accuracy, mastery, previous_mastery, mastery_delta, trend, retention_status, fluency, passive_knowledge, active_knowledge, confidence, error_count, repeated_error_count, days_since)
return {
"skill_tag": skill_tag,
"attempts": total_attempts,
"accuracy": round(accuracy, 1),
"recent_accuracy": round(recent_accuracy, 1),
"recognition_score": recognition_score,
"production_score": production_score,
"application_score": application_score,
"mastery": mastery,
"mastery_delta": mastery_delta,
"trend": trend,
"retention_status": retention_status,
"fluency": fluency,
"passive_knowledge": passive_knowledge,
"active_knowledge": active_knowledge,
"confidence": confidence,
}
async def update_topic_progress(conn, user_id: int, topic_id: str):
"""Обновляет прогресс темы: lessons_total, lessons_completed, topic_mastery"""
# Общее количество уроков в теме
lessons_total = await conn.fetchval("""
SELECT COUNT(*) FROM learning_lessons WHERE topic_id = $1 AND is_active = TRUE
""", topic_id) or 0
# Количество завершённых уроков
lessons_completed = await conn.fetchval("""
SELECT COUNT(*) FROM user_lesson_progress WHERE user_id = $1 AND lesson_id IN (
SELECT lesson_id FROM learning_lessons WHERE topic_id = $2
) AND completed = TRUE
""", user_id, topic_id) or 0
# Тестовый результат
test_row = await conn.fetchrow("""
SELECT test_completed, test_score FROM user_topic_progress WHERE user_id = $1 AND topic_id = $2
""", user_id, topic_id)
test_completed = test_row["test_completed"] if test_row else False
test_score = float(test_row["test_score"]) if test_row and test_row["test_score"] else 0
# Среднее мастерство по навыкам темы
skills_avg = await conn.fetchval("""
SELECT COALESCE(AVG(mastery_score), 0) FROM user_skill_mastery
WHERE user_id = $1 AND skill_tag IN (
SELECT DISTINCT skill_tag FROM learning_questions WHERE topic_id = $2
)
""", user_id, topic_id) or 0
# Мастерство темы
if test_completed and test_score > 0:
topic_mastery = round(float(skills_avg) * 0.4 + float(test_score) * 0.6, 2)
else:
topic_mastery = round(float(skills_avg), 2)
# Обновляем
await conn.execute("""
INSERT INTO user_topic_progress (user_id, topic_id, lessons_total, lessons_completed, test_completed, test_score, topic_mastery)
VALUES ($1, $2, $3, $4, $5, $6, $7)
ON CONFLICT (user_id, topic_id) DO UPDATE SET
lessons_total = $3,
lessons_completed = $4,
test_completed = $5,
test_score = $6,
topic_mastery = $7
""", user_id, topic_id, lessons_total, lessons_completed, test_completed, test_score, topic_mastery)
return {
"lessons_total": lessons_total,
"lessons_completed": lessons_completed,
"test_completed": test_completed,
"test_score": round(test_score, 1),
"topic_mastery": topic_mastery,
}
async def update_ai_context(conn, user_id: int):
"""Обновляет AI-контекст пользователя: сильные/слабые навыки, общее мастерство, прогресс уровня"""
# Получаем текущий уровень
current_level = await conn.fetchval("""
SELECT current_level FROM users WHERE telegram_id = $1
""", user_id) or "A1"
# Все темы пользователя
topics = await conn.fetch("""
SELECT topic_id, topic_mastery, test_completed FROM user_topic_progress WHERE user_id = $1
""", user_id)
completed_topics = sum(1 for t in topics if t["test_completed"])
completed_lessons = await conn.fetchval("""
SELECT COUNT(*) FROM user_lesson_progress WHERE user_id = $1 AND completed = TRUE
""", user_id) or 0
# Всего тем в уровне
level_topics_total = await conn.fetchval("""
SELECT COUNT(*) FROM learning_topics WHERE level_code = $1 AND is_active = TRUE
""", current_level) or 0
# Процент завершения уровня
level_completion_percent = round((completed_topics / level_topics_total * 100), 2) if level_topics_total > 0 else 0
# Общее мастерство
overall_mastery = await conn.fetchval("""
SELECT COALESCE(AVG(topic_mastery), 0) FROM user_topic_progress WHERE user_id = $1
""", user_id) or 0
overall_mastery = float(overall_mastery) if overall_mastery else 0
# Сильные и слабые навыки — используем общий источник
strong_skills = await get_strong_skills(conn, user_id, limit=8)
weak_skills = await get_weak_skills(conn, user_id, limit=5)
strong_skill_tags = [s["skill_tag"] for s in strong_skills]
weak_skill_tags = [s["skill_tag"] for s in weak_skills]
# Частые ошибки
errors = await conn.fetch("""
SELECT wrong_because, COUNT(*) as cnt
FROM user_learning_answers
WHERE user_id = $1 AND wrong_because IS NOT NULL AND correct = FALSE
GROUP BY wrong_because
ORDER BY cnt DESC
LIMIT 5
""", user_id)
recurring_errors = [e["wrong_because"] for e in errors]
# Обновляем ai_user_context
await conn.execute("""
INSERT INTO ai_user_context (user_id, current_level, completed_topics, level_topics_total, level_completion_percent, completed_lessons, overall_mastery, strong_skills, weak_skills, recurring_errors, analytics_enabled, last_analysis_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, TRUE, NOW())
ON CONFLICT (user_id) DO UPDATE SET
current_level = $2,
completed_topics = $3,
level_topics_total = $4,
level_completion_percent = $5,
completed_lessons = $6,
overall_mastery = $7,
strong_skills = $8,
weak_skills = $9,
recurring_errors = $10,
analytics_enabled = TRUE,
last_analysis_at = NOW()
""", user_id, current_level, completed_topics, level_topics_total, level_completion_percent, completed_lessons, round(float(overall_mastery), 2), json.dumps(strong_skill_tags), json.dumps(weak_skill_tags), json.dumps(recurring_errors))
return {
"completed_topics": completed_topics,
"level_topics_total": level_topics_total,
"level_completion_percent": level_completion_percent,
"completed_lessons": completed_lessons,
"overall_mastery": round(float(overall_mastery), 2),
"strong_skills": strong_skill_tags,
"weak_skills": weak_skill_tags,
}
@router.get("/levels")
async def get_learning_levels(request: Request):
"""Получить все уровни обучения"""
await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
rows = await conn.fetch("SELECT * FROM learning_levels WHERE is_active = TRUE ORDER BY sort_order")
return {"levels": [dict(row) for row in rows]}
@router.get("/topics")
async def get_topics(request: Request, level: str = "A1"):
"""Получить темы для уровня"""
await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
rows = await conn.fetch("SELECT * FROM learning_topics WHERE level_code = $1 AND is_active = TRUE ORDER BY order_number", level)
return {"topics": [dict(row) for row in rows]}
@router.get("/topics/{topic_id}")
async def get_topic(request: Request, topic_id: str):
"""Получить информацию о теме и список уроков"""
telegram_id, _ = await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
topic = await conn.fetchrow("SELECT * FROM learning_topics WHERE topic_id = $1", topic_id)
if not topic:
raise HTTPException(404, "Topic not found")
lessons = await conn.fetch("SELECT * FROM learning_lessons WHERE topic_id = $1 AND is_active = TRUE ORDER BY order_number", topic_id)
progress = await conn.fetchrow("SELECT * FROM user_topic_progress WHERE user_id = $1 AND topic_id = $2", telegram_id, topic_id)
lesson_progress = await conn.fetch("""
SELECT lesson_id, completed FROM user_lesson_progress WHERE user_id = $1
""", telegram_id)
progress_map = {lp["lesson_id"]: lp["completed"] for lp in lesson_progress}
lessons_data = []
for lesson in lessons:
l = dict(lesson)
l["completed"] = progress_map.get(lesson["lesson_id"], False)
lessons_data.append(l)
return {
"topic": dict(topic),
"lessons": lessons_data,
"progress": dict(progress) if progress else None,
}
@router.get("/lessons/{lesson_id}")
async def get_lesson(request: Request, lesson_id: str):
"""Получить урок с примерами и вопросами"""
telegram_id, _ = await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
lesson = await conn.fetchrow("SELECT * FROM learning_lessons WHERE lesson_id = $1", lesson_id)
if not lesson:
raise HTTPException(404, "Lesson not found")
examples = await conn.fetch("SELECT * FROM lesson_examples WHERE lesson_id = $1 ORDER BY order_number", lesson_id)
questions = await conn.fetch("SELECT * FROM learning_questions WHERE lesson_id = $1 AND is_test_question = FALSE ORDER BY id", lesson_id)
questions_data = []
for q in questions:
options = await conn.fetch("SELECT * FROM question_options WHERE question_id = $1", q["question_id"])
questions_data.append({
"question": dict(q),
"options": [dict(o) for o in options],
})
progress = await conn.fetchrow("SELECT * FROM user_lesson_progress WHERE user_id = $1 AND lesson_id = $2", telegram_id, lesson_id)
return {
"lesson": dict(lesson),
"examples": [dict(e) for e in examples],
"questions": questions_data,
"progress": dict(progress) if progress else None,
}
@router.get("/test/{topic_id}")
async def get_topic_test(request: Request, topic_id: str):
"""Получить вопросы финального теста"""
await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
questions = await conn.fetch("SELECT * FROM learning_questions WHERE topic_id = $1 AND is_test_question = TRUE ORDER BY test_order", topic_id)
questions_data = []
for q in questions:
options = await conn.fetch("SELECT * FROM question_options WHERE question_id = $1", q["question_id"])
questions_data.append({
"question": dict(q),
"options": [dict(o) for o in options],
})
return {"test": questions_data, "total": len(questions_data)}
@router.post("/session/start")
async def start_session(request: Request):
"""Начать новую сессию обучения"""
telegram_id, _ = await require_auth(request)
body = await request.json()
level_code = body.get("level_code", "A1")
topic_id = body.get("topic_id", "")
session_type = body.get("session_type", "lesson")
pool = await get_pool()
async with pool.acquire() as conn:
row = await conn.fetchrow("""
INSERT INTO learning_sessions (user_id, level_code, topic_id, session_type)
VALUES ($1, $2, $3, $4) RETURNING id
""", telegram_id, level_code, topic_id, session_type)
return {"session_id": row["id"]}
@router.post("/answer")
async def save_answer(request: Request):
"""Сохранить ответ пользователя"""
telegram_id, _ = await require_auth(request)
body = await request.json()
session_id = body.get("session_id")
question_id = body.get("question_id")
selected_answer = body.get("answer", "")
time_taken_ms = body.get("time_taken_ms", 0)
is_final = body.get("is_final_answer", True)
if not session_id or not question_id:
raise HTTPException(400, "session_id and question_id required")
pool = await get_pool()
async with pool.acquire() as conn:
question = await conn.fetchrow("SELECT * FROM learning_questions WHERE question_id = $1", question_id)
if not question:
raise HTTPException(404, "Question not found")
correct = selected_answer.strip().lower() == (question["correct_answer"] or "").strip().lower()
wrong_because = None
if not correct:
option = await conn.fetchrow("""
SELECT wrong_because FROM question_options
WHERE question_id = $1 AND option_key = $2
""", question_id, selected_answer)
if option:
wrong_because = option["wrong_because"]
session = await conn.fetchrow("SELECT * FROM learning_sessions WHERE id = $1", session_id)
await conn.execute("""
INSERT INTO user_learning_answers (user_id, session_id, level_code, topic_id, lesson_id, question_id, selected_answer, correct, skill_tag, question_type, context_type, difficulty, time_taken_ms, hint_used, wrong_because, is_final_answer)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
""", telegram_id, session_id, session["level_code"], session["topic_id"], question["lesson_id"], question_id, selected_answer, correct, question["skill_tag"], question["question_type"], question["context_type"], question["difficulty"], time_taken_ms, False, wrong_because, is_final)
return {"correct": correct, "wrong_because": wrong_because}
@router.post("/session/complete")
async def complete_session(request: Request):
"""Завершить сессию: обновляет прогресс урока/теста, мастерство навыков, прогресс темы и AI-контекст"""
telegram_id, _ = await require_auth(request)
body = await request.json()
session_id = body.get("session_id")
pool = await get_pool()
async with pool.acquire() as conn:
session = await conn.fetchrow("SELECT * FROM learning_sessions WHERE id = $1 AND user_id = $2", session_id, telegram_id)
if not session:
raise HTTPException(404, "Session not found")
await conn.execute("UPDATE learning_sessions SET is_completed = TRUE, completed_at = NOW() WHERE id = $1", session_id)
stats = await conn.fetchrow("""
SELECT COUNT(*) as total, COALESCE(SUM(CASE WHEN correct THEN 1 ELSE 0 END), 0) as correct_count
FROM user_learning_answers WHERE session_id = $1 AND is_final_answer = TRUE
""", session_id)
topic_id = session["topic_id"]
# Обновляем прогресс урока
if session["session_type"] == "lesson":
lesson_row = await conn.fetchrow("""
SELECT DISTINCT lesson_id FROM user_learning_answers WHERE session_id = $1 AND lesson_id IS NOT NULL
""", session_id)
if lesson_row and lesson_row["lesson_id"]:
lesson_id = lesson_row["lesson_id"]
score = (stats["correct_count"] / stats["total"] * 100) if stats["total"] > 0 else 0
await conn.execute("""
INSERT INTO user_lesson_progress (user_id, lesson_id, started_at, completed_at, attempts_count, correct_count, best_score, completed)
VALUES ($1, $2, NOW(), NOW(), 1, $3, $4, TRUE)
ON CONFLICT (user_id, lesson_id) DO UPDATE
SET completed_at = NOW(),
attempts_count = user_lesson_progress.attempts_count + 1,
correct_count = user_lesson_progress.correct_count + $3,
best_score = GREATEST(user_lesson_progress.best_score, $4),
completed = TRUE
""", telegram_id, lesson_id, stats["correct_count"], score)
# Обновляем прогресс темы если это тест
if session["session_type"] == "topic_test":
test_score = (stats["correct_count"] / stats["total"] * 100) if stats["total"] > 0 else 0
await conn.execute("""
INSERT INTO user_topic_progress (user_id, topic_id, test_started, test_completed, test_score, completed_at)
VALUES ($1, $2, TRUE, TRUE, $3, NOW())
ON CONFLICT (user_id, topic_id) DO UPDATE
SET test_completed = TRUE,
test_score = $3,
completed_at = NOW()
""", telegram_id, topic_id, test_score)
# Обновляем мастерство по всем навыкам из этой сессии
skill_tags = await conn.fetch("""
SELECT DISTINCT skill_tag FROM user_learning_answers WHERE session_id = $1
""", session_id)
for st in skill_tags:
await update_skill_mastery(conn, telegram_id, st["skill_tag"])
# Обновляем прогресс темы ТОЛЬКО для lesson и topic_test
if session["session_type"] in ("lesson", "topic_test") and topic_id:
await update_topic_progress(conn, telegram_id, topic_id)
# Обновляем AI-контекст (всегда)
await update_ai_context(conn, telegram_id)
return {"status": "ok", "total": stats["total"], "correct_count": stats["correct_count"]}
@router.get("/progress")
async def get_learning_progress(request: Request):
"""Получить прогресс пользователя по всем темам"""
telegram_id, _ = await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
topics = await conn.fetch("""
SELECT tp.*, up.lessons_completed, up.lessons_total, up.test_completed, up.test_score, up.topic_mastery
FROM learning_topics tp
LEFT JOIN user_topic_progress up ON tp.topic_id = up.topic_id AND up.user_id = $1
ORDER BY tp.level_code, tp.order_number
""", telegram_id)
return {"progress": [dict(t) for t in topics]}
@router.post("/analytics")
async def get_analytics(request: Request):
"""Анализирует результаты пользователя через AI на основе user_skill_mastery и user_topic_progress"""
telegram_id, _ = await require_auth(request)
if not TOGETHER_API_KEY:
raise HTTPException(500, "AI API key not configured")
pool = await get_pool()
async with pool.acquire() as conn:
# Получаем слабые навыки из ОБЩЕГО источника
weak_skills = await get_weak_skills(conn, telegram_id, limit=5)
if not weak_skills:
return {"analytics_enabled": False, "message": "Отличная работа! У тебя нет слабых навыков."}
# Загружаем названия навыков из БД
skill_names = await get_skill_names(conn)
skill_tag_lines = '\n'.join([f'- {tag}"{name}"' for tag, name in skill_names.items()])
# Находим уроки для перепрохождения
weak_skill_tags = [s["skill_tag"] for s in weak_skills]
weak_lessons = await get_weak_lessons(conn, weak_skill_tags)
weak_lessons_lines = '\n'.join([f'- {l["title_ru"]} (тема: {l["topic_name"]})' for l in weak_lessons])
# Формируем профиль
weak_skills_json = []
for s in weak_skills:
weak_skills_json.append({
"skill": s["skill_tag"],
"mastery": round(float(s["mastery_score"]), 1) if s["mastery_score"] else 0,
"repeated_errors": s["repeated_error_count"] or 0,
"retention": s["retention_status"] or "fresh",
"trend": s["trend"] or "stable",
"recent_accuracy": round(float(s["recent_accuracy"]), 1) if s["recent_accuracy"] else 0,
})
user_profile = {
"weak_skills": weak_skills_json,
"weak_lessons": [dict(l) for l in weak_lessons],
}
system_prompt = f"""Ты — AI-наставник польского языка в приложении Filwords PL.
Проанализируй слабые стороны пользователя и скажи что нужно перепройти.
Понятные названия навыков (используй ИХ в ответе, а не технические ID):
{skill_tag_lines}
ПРАВИЛА:
1. Не используй markdown
2. Отвечай на языке пользователя
3. НЕ говори о сильных сторонах — только о слабых
4. Говори ТОЛЬКО о тех навыках, которые перечислены в weak_skills
5. Для каждого слабого навыка укажи конкретный урок для перепрохождения
6. НАЗВАНИЯ НАВЫКОВ: всегда заменяй технические ID на понятные названия
7. ОБРАЩАЙСЯ К ПОЛЬЗОВАТЕЛЮ НА "ТЫ"
8. НИКОГДА не пиши имена метрик: mastery, recent_accuracy, retention_status и т.п. — только русский перевод
9. НЕ упоминай процент завершения уровня или количество пройденных тем
10. В конце предложи нажать кнопку "Потренировать слабые темы" — она уже есть под этим сообщением."""
user_prompt = f"""Слабые навыки пользователя:
{json.dumps(user_profile, ensure_ascii=False, indent=2)}
Напиши:
1. Какие темы ты не понял (конкретные навыки, понятными названиями)
2. Какие уроки нужно перепройти:
{weak_lessons_lines}
3. Нажми кнопку "Потренировать слабые темы" для практики
В конце напиши: "Готов потренироваться? Нажимай кнопку ниже!\""""
async with httpx.AsyncClient(timeout=90.0) as client:
response = await client.post(
TOGETHER_API_URL,
headers={
"Authorization": f"Bearer {TOGETHER_API_KEY}",
"Content-Type": "application/json",
},
json={
"model": ANALYTICS_MODEL,
"messages": [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt},
],
"max_tokens": 1000,
"temperature": 0.7,
},
)
if response.status_code != 200:
raise HTTPException(500, f"AI API error: {response.status_code}")
data = response.json()
ai_response = data["choices"][0]["message"]["content"].strip()
ai_response = ai_response.replace("**", "").replace("*", "").replace("#", "")
# Убираем технические ID в скобках
ai_response = re.sub(r'\s*\([a-z_]+_[a-z_]+\)', '', ai_response)
# Если ответ пустой - запасной текст
if not ai_response or len(ai_response) < 10:
ai_response = "Перепройди уроки по слабым темам. Нажми кнопку ниже, чтобы потренироваться!"
return {
"analytics_enabled": True,
"response": ai_response,
"weak_skills": weak_skills_json,
"has_practice_available": len(weak_skills_json) > 0,
"cta_label": "Потренировать слабые темы",
}
@router.get("/ai/context")
async def get_ai_context(request: Request):
"""Получить AI-контекст пользователя"""
telegram_id, _ = await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
context = await conn.fetchrow("SELECT * FROM ai_user_context WHERE user_id = $1", telegram_id)
if not context:
return {"analytics_enabled": False, "message": "Пройди первую тему, и я смогу начать анализировать твой прогресс."}
return {"context": dict(context)}
@router.post("/practice/start")
async def start_weak_skills_practice(request: Request):
"""Собирает сессию практики из реального банка вопросов по слабым навыкам.
Ноль обращений к LLM - только SQL."""
telegram_id, _ = await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
weak = await get_weak_skills(conn, telegram_id, limit=5)
if not weak:
return {"has_weak_skills": False, "message": "Нет слабых навыков - отличная работа!"}
skill_tags = [s["skill_tag"] for s in weak]
# Забираем краткое объяснение правила для каждого слабого навыка
explanations = await conn.fetch("""
SELECT DISTINCT ON (skill_tag) skill_tag, explanation_ru, explanation_uk, explanation_en, explanation_pl
FROM learning_lessons
WHERE skill_tag = ANY($1)
ORDER BY skill_tag, order_number
""", skill_tags)
explanations_map = {e["skill_tag"]: dict(e) for e in explanations}
# Вопросы вперемешку по всем слабым навыкам, только не тестовые
questions = await conn.fetch("""
SELECT * FROM learning_questions
WHERE skill_tag = ANY($1) AND is_test_question = FALSE
ORDER BY random()
LIMIT 10
""", skill_tags)
if not questions:
return {"has_weak_skills": True, "questions": [], "message": "Для этих навыков пока нет вопросов в базе."}
# Получаем уровень пользователя
user_level = await conn.fetchval("""
SELECT current_level FROM users WHERE telegram_id = $1
""", telegram_id) or "A1"
session = await conn.fetchrow("""
INSERT INTO learning_sessions (user_id, level_code, topic_id, session_type)
VALUES ($1, $2, '', 'weak_skills_practice') RETURNING id
""", telegram_id, user_level)
questions_data = []
for q in questions:
options = await conn.fetch(
"SELECT * FROM question_options WHERE question_id = $1", q["question_id"]
)
questions_data.append({"question": dict(q), "options": [dict(o) for o in options]})
# Загружаем понятные названия навыков из БД
skill_names = await get_skill_names(conn)
return {
"has_weak_skills": True,
"session_id": session["id"],
"explanations": [
{"skill_tag": tag, "name": skill_names.get(tag, tag), **explanations_map.get(tag, {})}
for tag in skill_tags
],
"questions": questions_data,
}
+40
View File
@@ -0,0 +1,40 @@
"""
Learning Data Package — данные всех уровней.
Каждый уровень в отдельном файле.
"""
import logging
from database import get_pool
from . import level_a1, level_a2, level_b1, level_b2, level_c1, level_c2
logger = logging.getLogger(__name__)
async def init_learning_data():
"""Инициализирует уровни и все темы"""
pool = await get_pool()
async with pool.acquire() as conn:
# ============ УРОВНИ ============
levels = [
("A1", "Poziom A1", "Уровень A1", "Рівень A1", "Level A1", 1),
("A2", "Poziom A2", "Уровень A2", "Рівень A2", "Level A2", 2),
("B1", "Poziom B1", "Уровень B1", "Рівень B1", "Level B1", 3),
("B2", "Poziom B2", "Уровень B2", "Рівень B2", "Level B2", 4),
("C1", "Poziom C1", "Уровень C1", "Рівень C1", "Level C1", 5),
("C2", "Poziom C2", "Уровень C2", "Рівень C2", "Level C2", 6),
]
for code, pl, ru, uk, en, sort in levels:
await conn.execute("""
INSERT INTO learning_levels (code, name_pl, name_ru, name_uk, name_en, sort_order)
VALUES ($1, $2, $3, $4, $5, $6)
ON CONFLICT (code) DO NOTHING
""", code, pl, ru, uk, en, sort)
# Загружаем данные каждого уровня
await level_a1.init_level_a1(conn)
await level_a2.init_level_a2(conn)
await level_b1.init_level_b1(conn)
await level_b2.init_level_b2(conn)
await level_c1.init_level_c1(conn)
await level_c2.init_level_c2(conn)
logger.info("Learning data fully initialized")
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+3
View File
@@ -0,0 +1,3 @@
async def init_level_b1(conn):
"""Уровень B1 — пока пусто"""
pass
+3
View File
@@ -0,0 +1,3 @@
async def init_level_b2(conn):
"""Уровень B2 — пока пусто"""
pass
+3
View File
@@ -0,0 +1,3 @@
async def init_level_c1(conn):
"""Уровень C1 — пока пусто"""
pass
+3
View File
@@ -0,0 +1,3 @@
async def init_level_c2(conn):
"""Уровень C2 — пока пусто"""
pass
+56
View File
@@ -0,0 +1,56 @@
"""
Единый профиль ученика источник правды для аналитики и повторения.
Никакой LLM здесь нет только детерминированная бизнес-логика.
"""
import hashlib
import json
from decimal import Decimal
async def get_weak_skills(conn, user_id: int, limit: int = 5):
"""Получить слабые навыки (mastery < 75 или needs_review/overdue или repeated_error).
Одинаково используется в /analytics и /review."""
rows = await conn.fetch("""
SELECT skill_tag, mastery_score, repeated_error_count,
retention_status, trend, recent_accuracy,
recognition_score, production_score, application_score,
median_correct_time_ms, days_since_last_attempt
FROM user_skill_mastery
WHERE user_id = $1 AND attempts_count >= 2
AND (mastery_score < 75
OR retention_status IN ('needs_review', 'overdue')
OR repeated_error_count > 0)
ORDER BY mastery_score ASC
LIMIT $2
""", user_id, limit)
return [dict(r) for r in rows]
async def get_strong_skills(conn, user_id: int, limit: int = 8):
"""Получить сильные навыки (mastery >= 75)."""
rows = await conn.fetch("""
SELECT skill_tag, mastery_score FROM user_skill_mastery
WHERE user_id = $1 AND attempts_count >= 2 AND mastery_score >= 75
ORDER BY mastery_score DESC
LIMIT $2
""", user_id, limit)
return [dict(r) for r in rows]
def get_profile_hash(weak_skills: list) -> str:
"""Хэш списка слабых навыков — для инвалидации кэша."""
# Конвертируем Decimal в float для JSON
serializable = []
for s in weak_skills:
item = {}
for k, v in s.items():
if isinstance(v, Decimal):
item[k] = float(v)
else:
item[k] = v
serializable.append(item)
canonical = json.dumps(serializable, sort_keys=True, ensure_ascii=False)
return hashlib.sha256(canonical.encode()).hexdigest()[:16]
+286
View File
@@ -0,0 +1,286 @@
"""
Orchestrator мозг чат-учителя.
Выбирает следующий урок для пользователя на основе:
1. retention_status = 'overdue' или 'needs_review' повторение
2. mastery_score < 75 повторение слабого навыка
3. Иначе следующий урок по порядку
Никакого AI только детерминированная логика.
"""
import logging
from typing import Optional, Dict, Any
logger = logging.getLogger(__name__)
async def get_next_lesson(conn, user_id: int) -> Dict[str, Any]:
"""
Выбирает следующий урок для пользователя.
Возвращает:
{
"type": "new" | "review" | "test",
"lesson": {...} | None,
"reason": str,
"skill_tag": str | None
}
"""
# Получаем текущий уровень пользователя
current_level = await conn.fetchval("""
SELECT current_level FROM users WHERE telegram_id = $1
""", user_id) or "A1"
# ============ ШАГ 1: Проверяем retention_status ============
# Если есть навыки, которые давно не повторяли — повторяем их
overdue_skill = await conn.fetchrow("""
SELECT skill_tag, mastery_score, days_since_last_attempt, retention_status
FROM user_skill_mastery
WHERE user_id = $1
AND retention_status IN ('overdue', 'needs_review')
AND attempts_count >= 2
ORDER BY days_since_last_attempt DESC
LIMIT 1
""", user_id)
if overdue_skill:
lesson = await conn.fetchrow("""
SELECT l.lesson_id, l.topic_id, l.skill_tag, l.title_pl, l.title_ru, l.title_uk, l.title_en
FROM learning_lessons l
WHERE l.skill_tag = $1
AND l.is_active = TRUE
ORDER BY l.order_number
LIMIT 1
""", overdue_skill["skill_tag"])
if lesson:
logger.info(f"Orchestrator: review (overdue) for user={user_id}, skill={overdue_skill['skill_tag']}")
return {
"type": "review",
"lesson": dict(lesson),
"reason": "overdue",
"skill_tag": overdue_skill["skill_tag"],
}
# ============ ШАГ 2: Проверяем mastery < 75 ============
weak_skill = await conn.fetchrow("""
SELECT skill_tag, mastery_score, repeated_error_count, trend
FROM user_skill_mastery
WHERE user_id = $1
AND mastery_score < 75
AND attempts_count >= 2
ORDER BY mastery_score ASC
LIMIT 1
""", user_id)
if weak_skill:
lesson = await conn.fetchrow("""
SELECT l.lesson_id, l.topic_id, l.skill_tag, l.title_pl, l.title_ru, l.title_uk, l.title_en
FROM learning_lessons l
WHERE l.skill_tag = $1
AND l.is_active = TRUE
ORDER BY l.order_number
LIMIT 1
""", weak_skill["skill_tag"])
if lesson:
logger.info(f"Orchestrator: review (weak) for user={user_id}, skill={weak_skill['skill_tag']}, mastery={weak_skill['mastery_score']}")
return {
"type": "review",
"lesson": dict(lesson),
"reason": "weak_mastery",
"skill_tag": weak_skill["skill_tag"],
}
# ============ ШАГ 3: Проверяем, нужно ли пройти тест темы ============
# Если пользователь завершил все уроки темы, но не прошёл тест
topic_for_test = await conn.fetchrow("""
SELECT tp.topic_id, tp.lessons_total, tp.lessons_completed, tp.test_completed
FROM user_topic_progress tp
WHERE tp.user_id = $1
AND tp.lessons_completed >= tp.lessons_total
AND tp.lessons_total > 0
AND tp.test_completed = FALSE
ORDER BY tp.topic_id
LIMIT 1
""", user_id)
if topic_for_test:
logger.info(f"Orchestrator: test for user={user_id}, topic={topic_for_test['topic_id']}")
return {
"type": "test",
"lesson": None,
"reason": "topic_completed_no_test",
"topic_id": topic_for_test["topic_id"],
}
# ============ ШАГ 4: Следующий урок по порядку ============
# Находим первый непройденный урок в текущем уровне
next_lesson = await conn.fetchrow("""
SELECT l.lesson_id, l.topic_id, l.skill_tag, l.title_pl, l.title_ru, l.title_uk, l.title_en,
t.topic_id as topic_id, t.order_number as topic_order
FROM learning_lessons l
JOIN learning_topics t ON l.topic_id = t.topic_id
WHERE t.level_code = $1
AND l.is_active = TRUE
AND l.lesson_id NOT IN (
SELECT lesson_id FROM user_lesson_progress
WHERE user_id = $2 AND completed = TRUE
)
ORDER BY t.order_number, l.order_number
LIMIT 1
""", current_level, user_id)
if next_lesson:
logger.info(f"Orchestrator: new lesson for user={user_id}, lesson={next_lesson['lesson_id']}")
return {
"type": "new",
"lesson": dict(next_lesson),
"reason": "next_in_order",
"skill_tag": next_lesson["skill_tag"],
}
# ============ ШАГ 5: Всё пройдено ============
logger.info(f"Orchestrator: all completed for user={user_id}")
return {
"type": "completed",
"lesson": None,
"reason": "all_completed",
"skill_tag": None,
}
async def get_lesson_by_id(conn, lesson_id: str) -> Optional[Dict[str, Any]]:
"""Получает урок по ID со всеми данными: объяснение, примеры, вопросы."""
lesson = await conn.fetchrow("""
SELECT * FROM learning_lessons WHERE lesson_id = $1 AND is_active = TRUE
""", lesson_id)
if not lesson:
return None
examples = await conn.fetch("""
SELECT * FROM lesson_examples WHERE lesson_id = $1 ORDER BY order_number
""", lesson_id)
questions = await conn.fetch("""
SELECT * FROM learning_questions
WHERE lesson_id = $1 AND is_test_question = FALSE
ORDER BY id
""", lesson_id)
questions_data = []
for q in questions:
options = await conn.fetch("""
SELECT * FROM question_options WHERE question_id = $1
""", q["question_id"])
questions_data.append({
"question": dict(q),
"options": [dict(o) for o in options],
})
return {
"lesson": dict(lesson),
"examples": [dict(e) for e in examples],
"questions": questions_data,
}
async def get_lesson_questions(conn, lesson_id: str) -> list:
"""Получает только вопросы урока (без объяснения)."""
questions = await conn.fetch("""
SELECT * FROM learning_questions
WHERE lesson_id = $1 AND is_test_question = FALSE
ORDER BY id
""", lesson_id)
questions_data = []
for q in questions:
options = await conn.fetch("""
SELECT * FROM question_options WHERE question_id = $1
""", q["question_id"])
questions_data.append({
"question": dict(q),
"options": [dict(o) for o in options],
})
return questions_data
async def get_test_questions(conn, topic_id: str) -> list:
"""Получает вопросы финального теста по теме."""
questions = await conn.fetch("""
SELECT * FROM learning_questions
WHERE topic_id = $1 AND is_test_question = TRUE
ORDER BY test_order
""", topic_id)
questions_data = []
for q in questions:
options = await conn.fetch("""
SELECT * FROM question_options WHERE question_id = $1
""", q["question_id"])
questions_data.append({
"question": dict(q),
"options": [dict(o) for o in options],
})
return questions_data
async def get_random_reaction(conn, reaction_type: str, lang: str) -> str:
"""Получает случайную реакцию из банка по типу."""
row = await conn.fetchrow("""
SELECT text_pl, text_ru, text_uk, text_en
FROM teacher_reactions
WHERE reaction_type = $1
ORDER BY RANDOM()
LIMIT 1
""", reaction_type)
if not row:
# Запасные реакции, если таблица пуста
fallbacks = {
"correct": {
"pl": "Świetnie!",
"ru": "Отлично!",
"uk": "Чудово!",
"en": "Great!"
},
"almost": {
"pl": "Prawie!",
"ru": "Почти!",
"uk": "Майже!",
"en": "Almost!"
},
"wrong": {
"pl": "Nie tym razem.",
"ru": "Не в этот раз.",
"uk": "Не цього разу.",
"en": "Not this time."
},
}
return fallbacks.get(reaction_type, {}).get(lang, "OK")
field = f"text_{lang}"
return row.get(field) or row.get("text_en") or "OK"
async def get_wrong_because_explanation(conn, wrong_because: str, lang: str) -> str:
"""Получает объяснение ошибки по тегу wrong_because."""
row = await conn.fetchrow("""
SELECT explanation_pl, explanation_ru, explanation_uk, explanation_en
FROM wrong_because_explanations
WHERE wrong_because = $1
""", wrong_because)
if not row:
return ""
field = f"explanation_{lang}"
return row.get(field) or row.get("explanation_en") or ""
+46
View File
@@ -0,0 +1,46 @@
import random
from fastapi import APIRouter, Request, HTTPException
from auth import require_auth
from database import get_pool
router = APIRouter(prefix="/pronunciation", tags=["pronunciation"])
@router.get("/next")
async def get_next_word(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 FROM users WHERE telegram_id = $1", telegram_id
)
if not user:
raise HTTPException(401, "User not registered")
lang = user["app_language"] or "pl"
# Берём случайное слово из всех уровней (без привязки к конкретному уровню)
word = await conn.fetchrow("""
SELECT word_pl, translation_ru, translation_uk, translation_en, level
FROM (
SELECT DISTINCT ON (word_pl) word_pl, translation_ru, translation_uk, translation_en, level
FROM level_words
WHERE level_id IS NULL
) AS unique_words
ORDER BY RANDOM()
LIMIT 1
""")
if not word:
raise HTTPException(404, "No words found")
translation_field = f"translation_{lang}"
translation = word.get(translation_field) or word.get("translation_en") or word["word_pl"]
return {
"word_pl": word["word_pl"],
"translation": translation,
"level": word["level"] or "A1",
}
+290
View File
@@ -0,0 +1,290 @@
import csv
import random
from fastapi import APIRouter, Request, HTTPException
from auth import require_auth
from database import get_pool
router = APIRouter(prefix="/sentences", tags=["sentences"])
@router.get("/{sentence_id}/hint")
async def get_sentence_hint(sentence_id: int, request: Request):
"""Получить правильное предложение в качестве подсказки (одноразово)"""
telegram_id, _ = 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 {
"sentence_pl": row["sentence_pl"],
}
def load_sentences_from_csv(filename):
"""Загружает предложения из CSV файла (формат: polish,english,russian,ukrainian)."""
sentences = []
with open(filename, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
sentences.append({
"sentence_pl": row["polish"].strip(),
"translation_ru": row.get("russian", ""),
"translation_uk": row.get("ukrainian", ""),
"translation_en": row.get("english", ""),
})
return sentences
async def init_sentences():
"""Инициализирует таблицу предложений из CSV файлов по уровням."""
pool = await get_pool()
async with pool.acquire() as conn:
level_files = [
("A1", "sentences_a1.csv"),
("A2", "sentences_a2.csv"),
("B1", "sentences_b1.csv"),
]
for level, filename in level_files:
sentences = load_sentences_from_csv(filename)
for i, s in enumerate(sentences):
words_count = len(s["sentence_pl"].split())
difficulty = 1 if words_count <= 4 else 2 if words_count <= 6 else 3
await conn.execute(
"""INSERT INTO sentences (sentence_number, sentence_pl, translation_ru, translation_uk, translation_en, difficulty, level)
VALUES ($1, $2, $3, $4, $5, $6, $7)
ON CONFLICT (level, sentence_number) DO NOTHING""",
i + 1, s["sentence_pl"],
s["translation_ru"], s["translation_uk"], s["translation_en"],
difficulty, level
)
def normalize_word_for_display(word: str) -> str:
"""Убирает знаки конца предложения и приводит к нижнему регистру.
Запятые, точки с запятой, двоеточия и кавычки ОСТАЮТСЯ игрок должен их видеть."""
return word.strip('.!?…').lower()
def normalize_word_for_check(word: str) -> str:
"""Убирает ВСЮ пунктуацию для сравнения — игрок не обязан расставлять запятые."""
return word.strip('.,!?;:()[]{}"\'-…').lower()
@router.get("/next")
async def get_next_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.* FROM sentences s
WHERE s.level = $1
AND s.id NOT IN (
SELECT sentence_id FROM sentence_progress WHERE user_id = $2
)
ORDER BY RANDOM()
LIMIT 1
""", current_level, telegram_id)
if not row:
return {"completed": True, "message": "All sentences completed!"}
# Оригинальные слова (для проверки)
correct_words = row["sentence_pl"].split()
# Слова для ОТОБРАЖЕНИЯ — запятые остаются, убираем только точки/воскл/вопрос
display_words = [normalize_word_for_display(w) for w in correct_words]
shuffled_words = display_words.copy()
random.shuffle(shuffled_words)
translation_field = f"translation_{lang}"
translation = row.get(translation_field, "") or row.get("translation_en", "")
return {
"id": row["id"],
"sentence_number": row["sentence_number"],
"words": shuffled_words,
"correct_words": display_words,
"translation": translation,
"difficulty": row["difficulty"],
"level": row["level"],
}
@router.post("/check")
async def check_sentence(request: Request):
"""Проверка предложения с защитой от повторного прохождения и выдачей достижений"""
telegram_id, _ = await require_auth(request)
body = await request.json()
sentence_id = body.get("sentence_id")
user_words = body.get("words", [])
completion_time = body.get("completion_time")
if not sentence_id or not user_words:
raise HTTPException(400, "sentence_id and words required")
if len(user_words) > 20:
raise HTTPException(400, "Too many words")
pool = await get_pool()
async with pool.acquire() as conn:
user_exists = await conn.fetchval(
"SELECT 1 FROM users WHERE telegram_id = $1", telegram_id
)
if not user_exists:
raise HTTPException(401, "User not registered")
row = await conn.fetchrow(
"SELECT id, sentence_pl, level FROM sentences WHERE id = $1", sentence_id
)
if not row:
raise HTTPException(404, "Sentence not found")
existing_progress = await conn.fetchrow(
"SELECT score, completed_at FROM sentence_progress WHERE user_id = $1 AND sentence_id = $2",
telegram_id, sentence_id
)
if existing_progress:
return {
"correct": True,
"correct_words": [normalize_word_for_display(w) for w in row["sentence_pl"].split()],
"already_completed": True,
"score": existing_progress["score"],
"completed_at": existing_progress["completed_at"].isoformat() if existing_progress["completed_at"] else None,
"new_achievements": []
}
# Защита от читерства: минимальное время 3 секунды
if completion_time is not None and completion_time < 3:
return {
"correct": False,
"correct_words": [normalize_word_for_display(w) for w in row["sentence_pl"].split()],
"already_completed": False,
"new_achievements": [],
"error": "Too fast. Please take your time."
}
# Нормализуем для СРАВНЕНИЯ — без запятых и прочей пунктуации
correct_words_original = row["sentence_pl"].split()
correct_words_normalized = [normalize_word_for_check(w) for w in correct_words_original]
user_words_normalized = [normalize_word_for_check(w) for w in user_words]
is_correct = user_words_normalized == correct_words_normalized
if is_correct:
word_count = len(correct_words_original)
if word_count <= 4:
score = 50
elif word_count <= 6:
score = 75
else:
score = 100
await conn.execute(
"""INSERT INTO sentence_progress (user_id, sentence_id, completed_at, score, level)
VALUES ($1, $2, NOW(), $3, $4)""",
telegram_id, sentence_id, score, row["level"],
)
from routers.achievements import check_and_award_achievements
new_achievements = []
sentences_completed = await conn.fetchval(
"SELECT COUNT(*) FROM sentence_progress WHERE user_id = $1",
telegram_id
)
ach = await check_and_award_achievements(telegram_id, "sentences_completed", sentences_completed)
new_achievements.extend(ach)
if completion_time is not None and completion_time > 0:
ach = await check_and_award_achievements(telegram_id, "speed_sentence", int(completion_time))
new_achievements.extend(ach)
# Общий счёт по ВСЕМ играм
wordsearch_score = await conn.fetchval(
"SELECT COALESCE(SUM(score), 0) FROM user_progress WHERE user_id = $1 AND completed_at IS NOT NULL",
telegram_id
) or 0
sentence_score = await conn.fetchval(
"SELECT COALESCE(SUM(score), 0) FROM sentence_progress WHERE user_id = $1 AND completed_at IS NOT NULL",
telegram_id
) or 0
wordquiz_score = await conn.fetchval(
"SELECT COALESCE(total_score, 0) FROM word_quiz_progress WHERE user_id = $1",
telegram_id
) or 0
bubblewords_score = await conn.fetchval(
"SELECT COALESCE(total_score, 0) FROM bubble_words_progress WHERE user_id = $1",
telegram_id
) or 0
grand_total = wordsearch_score + sentence_score + wordquiz_score + bubblewords_score
ach = await check_and_award_achievements(telegram_id, "total_score", grand_total)
new_achievements.extend(ach)
return {
"correct": True,
"correct_words": [normalize_word_for_display(w) for w in correct_words_original],
"already_completed": False,
"score": score,
"new_achievements": new_achievements
}
return {
"correct": False,
"correct_words": [normalize_word_for_display(w) for w in correct_words_original],
"already_completed": False,
"new_achievements": []
}
@router.get("/stats")
async def get_sentence_stats(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
)
completed = await conn.fetchval(
"SELECT COUNT(*) FROM sentence_progress WHERE user_id = $1 AND level = $2",
telegram_id, current_level
)
total_score = await conn.fetchval(
"SELECT COALESCE(SUM(score), 0) FROM sentence_progress WHERE user_id = $1 AND level = $2",
telegram_id, current_level
)
return {
"total_sentences": total,
"completed": completed or 0,
"remaining": total - (completed or 0),
"total_score": total_score or 0,
"progress_percent": round(((completed or 0) / total * 100), 1) if total > 0 else 0,
"level": current_level,
}
+359
View File
@@ -0,0 +1,359 @@
"""
Support Router система поддержки пользователей.
Пользователи пишут админу о багах, неточностях и предложениях.
Админ отвечает. Обе стороны могут переводить сообщения через AI (MiniMax-M3).
"""
import logging
import httpx
import re
from fastapi import APIRouter, Request, HTTPException
from auth import require_auth
from database import get_pool
from config import TOGETHER_API_KEY, ADMIN_IDS
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/support", tags=["support"])
TOGETHER_API_URL = "https://api.together.xyz/v1/chat/completions"
SUPPORT_MODEL = "MiniMaxAI/MiniMax-M3"
async def call_ai_translate(text: str, target_lang: str) -> str:
"""Переводит текст через AI (MiniMax-M3)."""
if not TOGETHER_API_KEY:
return ""
try:
async with httpx.AsyncClient(timeout=90.0) as client:
response = await client.post(
TOGETHER_API_URL,
headers={
"Authorization": f"Bearer {TOGETHER_API_KEY}",
"Content-Type": "application/json",
},
json={
"model": SUPPORT_MODEL,
"messages": [
{"role": "system", "content": f"Ты — переводчик. Переведи текст на язык: {target_lang}. Отвечай только переводом без пояснений. Не используй markdown."},
{"role": "user", "content": text},
],
"max_tokens": 500,
"temperature": 0.3,
},
)
if response.status_code != 200:
logger.error(f"Translate API error: {response.status_code}")
return ""
data = response.json()
translated = data["choices"][0]["message"]["content"].strip()
translated = re.sub(r'\s*\([a-z_]+_[a-z_]+\)', '', translated)
return translated
except Exception as e:
logger.error(f"Translate error: {str(e)}")
return ""
async def get_user_lang(conn, user_id: int) -> str:
"""Получает язык интерфейса пользователя."""
lang = await conn.fetchval("""
SELECT app_language FROM users WHERE telegram_id = $1
""", user_id)
return lang if lang in ["pl", "ru", "uk", "en"] else "pl"
@router.post("/send")
async def send_support_message(request: Request):
"""
Пользователь отправляет сообщение в поддержку.
Если у пользователя нет открытого тикета создаётся новый.
"""
telegram_id, _ = await require_auth(request)
body = await request.json()
message = body.get("message", "").strip()
if not message:
raise HTTPException(400, "message is required")
if len(message) > 1000:
raise HTTPException(400, "Message too long (max 1000 characters)")
pool = await get_pool()
async with pool.acquire() as conn:
# Ищем открытый тикет пользователя
ticket = await conn.fetchrow("""
SELECT id FROM support_tickets
WHERE user_id = $1 AND status = 'open'
ORDER BY id DESC
LIMIT 1
""", telegram_id)
ticket_id = None
if ticket:
ticket_id = ticket["id"]
# Обновляем updated_at
await conn.execute("""
UPDATE support_tickets SET updated_at = NOW() WHERE id = $1
""", ticket_id)
else:
# Создаём новый тикет
new_ticket = await conn.fetchrow("""
INSERT INTO support_tickets (user_id, status)
VALUES ($1, 'open') RETURNING id
""", telegram_id)
ticket_id = new_ticket["id"]
# Сохраняем сообщение (юзер написал — админ ещё не прочитал)
await conn.execute("""
INSERT INTO support_messages (ticket_id, sender_id, message, is_read)
VALUES ($1, $2, $3, FALSE)
""", ticket_id, telegram_id, message)
return {"status": "ok", "ticket_id": ticket_id}
@router.get("/user-tickets")
async def get_user_tickets(request: Request):
"""Пользователь получает список своих тикетов."""
telegram_id, _ = await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
tickets = await conn.fetch("""
SELECT id, status, created_at, updated_at
FROM support_tickets
WHERE user_id = $1
ORDER BY updated_at DESC
""", telegram_id)
return {"tickets": [dict(t) for t in tickets]}
@router.get("/user-messages/{ticket_id}")
async def get_user_messages(request: Request, ticket_id: int):
"""Пользователь получает сообщения своего тикета и отмечает ответы админа прочитанными."""
telegram_id, _ = await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
# Проверяем, что тикет принадлежит пользователю
ticket = await conn.fetchrow("""
SELECT id FROM support_tickets WHERE id = $1 AND user_id = $2
""", ticket_id, telegram_id)
if not ticket:
raise HTTPException(404, "Ticket not found")
# Отмечаем сообщения админа как прочитанные
await conn.execute("""
UPDATE support_messages SET is_read = TRUE
WHERE ticket_id = $1 AND sender_id != $2
""", ticket_id, telegram_id)
messages = await conn.fetch("""
SELECT id, sender_id, message, is_read, created_at
FROM support_messages
WHERE ticket_id = $1
ORDER BY id ASC
""", ticket_id)
return {"messages": [dict(m) for m in messages]}
@router.get("/user-unread-count")
async def get_user_unread_count(request: Request):
"""Пользователь получает количество непрочитанных ответов админа."""
telegram_id, _ = await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
count = await conn.fetchval("""
SELECT COUNT(*) FROM support_messages m
JOIN support_tickets t ON m.ticket_id = t.id
WHERE t.user_id = $1
AND m.is_read = FALSE
AND m.sender_id != $1
""", telegram_id)
return {"unread_count": count or 0}
@router.post("/delete-ticket/{ticket_id}")
async def delete_ticket(request: Request, ticket_id: int):
"""
Удаляет тикет.
Может удалить: сам пользователь (свой тикет) или админ (любой).
"""
telegram_id, _ = await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
# Проверяем права
if telegram_id in ADMIN_IDS:
# Админ может удалить любой
result = await conn.execute("DELETE FROM support_tickets WHERE id = $1", ticket_id)
else:
# Пользователь может удалить только свой
result = await conn.execute("""
DELETE FROM support_tickets WHERE id = $1 AND user_id = $2
""", ticket_id, telegram_id)
affected = int(result.split()[-1])
if affected == 0:
raise HTTPException(404, "Ticket not found or no permission")
return {"status": "ok"}
# ============ АДМИНСКИЕ ЭНДПОИНТЫ ============
@router.get("/admin-tickets")
async def get_admin_tickets(request: Request):
"""Админ получает список всех тикетов."""
telegram_id, _ = await require_auth(request)
if telegram_id not in ADMIN_IDS:
raise HTTPException(403, "Access denied")
pool = await get_pool()
async with pool.acquire() as conn:
tickets = await conn.fetch("""
SELECT
t.id,
t.user_id,
t.status,
t.created_at,
t.updated_at,
u.username,
u.app_language,
(SELECT COUNT(*) FROM support_messages m WHERE m.ticket_id = t.id AND m.is_read = FALSE) as unread_count,
(SELECT message FROM support_messages m WHERE m.ticket_id = t.id ORDER BY m.id DESC LIMIT 1) as last_message
FROM support_tickets t
LEFT JOIN users u ON t.user_id = u.telegram_id
ORDER BY t.updated_at DESC
""")
return {"tickets": [dict(t) for t in tickets]}
@router.get("/admin-messages/{ticket_id}")
async def get_admin_messages(request: Request, ticket_id: int):
"""Админ получает сообщения тикета и отмечает их как прочитанные."""
telegram_id, _ = await require_auth(request)
if telegram_id not in ADMIN_IDS:
raise HTTPException(403, "Access denied")
pool = await get_pool()
async with pool.acquire() as conn:
ticket = await conn.fetchrow("""
SELECT id FROM support_tickets WHERE id = $1
""", ticket_id)
if not ticket:
raise HTTPException(404, "Ticket not found")
# Отмечаем сообщения пользователя как прочитанные
await conn.execute("""
UPDATE support_messages SET is_read = TRUE
WHERE ticket_id = $1 AND sender_id != $2
""", ticket_id, telegram_id)
messages = await conn.fetch("""
SELECT id, sender_id, message, is_read, created_at
FROM support_messages
WHERE ticket_id = $1
ORDER BY id ASC
""", ticket_id)
return {"messages": [dict(m) for m in messages]}
@router.post("/admin-reply")
async def admin_reply(request: Request):
"""Админ отвечает пользователю."""
telegram_id, _ = await require_auth(request)
if telegram_id not in ADMIN_IDS:
raise HTTPException(403, "Access denied")
body = await request.json()
ticket_id = body.get("ticket_id")
message = body.get("message", "").strip()
if not ticket_id or not message:
raise HTTPException(400, "ticket_id and message required")
if len(message) > 1000:
raise HTTPException(400, "Message too long (max 1000 characters)")
pool = await get_pool()
async with pool.acquire() as conn:
ticket = await conn.fetchrow("""
SELECT id FROM support_tickets WHERE id = $1
""", ticket_id)
if not ticket:
raise HTTPException(404, "Ticket not found")
# 🔧 ИСПРАВЛЕНО: админ написал — юзер ЕЩЁ НЕ ПРОЧИТАЛ, поэтому is_read = FALSE
# Раньше было TRUE, из-за чего бейдж непрочитанных у пользователя не появлялся
await conn.execute("""
INSERT INTO support_messages (ticket_id, sender_id, message, is_read)
VALUES ($1, $2, $3, FALSE)
""", ticket_id, telegram_id, message)
await conn.execute("""
UPDATE support_tickets SET updated_at = NOW() WHERE id = $1
""", ticket_id)
return {"status": "ok"}
@router.get("/unread-count")
async def get_unread_count(request: Request):
"""Админ получает количество непрочитанных сообщений."""
telegram_id, _ = await require_auth(request)
if telegram_id not in ADMIN_IDS:
raise HTTPException(403, "Access denied")
pool = await get_pool()
async with pool.acquire() as conn:
count = await conn.fetchval("""
SELECT COUNT(*) FROM support_messages
WHERE is_read = FALSE AND sender_id != $1
""", telegram_id)
return {"unread_count": count or 0}
@router.post("/translate")
async def translate_message(request: Request):
"""
Перевод сообщения через AI (MiniMax-M3).
Доступно и пользователю, и админу.
"""
telegram_id, _ = await require_auth(request)
body = await request.json()
text = body.get("text", "").strip()
target_lang = body.get("target_lang", "ru")
if not text:
raise HTTPException(400, "text is required")
if len(text) > 1000:
raise HTTPException(400, "Text too long (max 1000 characters)")
if target_lang not in ["pl", "ru", "uk", "en"]:
target_lang = "ru"
translated = await call_ai_translate(text, target_lang)
if not translated:
raise HTTPException(500, "Translation failed")
return {"translation": translated}
+1164
View File
File diff suppressed because it is too large Load Diff
+99
View File
@@ -0,0 +1,99 @@
"""
TTS Router озвучка текста через Edge TTS.
Бесплатный, качественный синтез речи для польского языка.
Кэш хранится в файлах на диске.
"""
import logging
import io
import os
import hashlib
import edge_tts
from fastapi import APIRouter, Request, HTTPException
from fastapi.responses import Response
from auth import require_auth
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/tts", tags=["tts"])
# Голоса Edge TTS для польского
VOICE_PL = "pl-PL-MarekNeural" # Мужской голос (основной)
VOICE_PL_FEMALE = "pl-PL-AgnieszkaNeural" # Женский голос
# Директория для кэша
CACHE_DIR = "/root/filwordspl/backend/tts_cache"
os.makedirs(CACHE_DIR, exist_ok=True)
def get_cache_path(voice_name: str, text: str) -> str:
"""Создаёт путь к файлу кэша на основе хеша текста и голоса"""
cache_key = hashlib.md5(f"{voice_name}:{text}".encode()).hexdigest()
return os.path.join(CACHE_DIR, f"{cache_key}.mp3")
@router.get("")
async def text_to_speech(request: Request, text: str = "", voice: str = "male"):
"""
Озвучивает текст через Edge TTS.
Параметры:
- text: текст для озвучки
- voice: male (Marek) или female (Agnieszka)
Возвращает: audio/mpeg
"""
await require_auth(request)
if not text or len(text) > 500:
raise HTTPException(400, "Text is required (max 500 characters)")
# Выбираем голос
if voice == "female":
voice_name = VOICE_PL_FEMALE
else:
voice_name = VOICE_PL
# Путь к файлу кэша
cache_path = get_cache_path(voice_name, text)
# Проверяем файловый кэш
if os.path.exists(cache_path):
logger.info(f"TTS: cache hit for text: {text[:50]}...")
with open(cache_path, 'rb') as f:
return Response(
content=f.read(),
media_type="audio/mpeg",
)
try:
logger.info(f"TTS: synthesizing text: {text[:50]}...")
# Синтезируем речь
communicate = edge_tts.Communicate(text, voice_name)
audio_data = io.BytesIO()
async for chunk in communicate.stream():
if chunk["type"] == "audio":
audio_data.write(chunk["data"])
audio_bytes = audio_data.getvalue()
if not audio_bytes:
raise HTTPException(500, "TTS generation failed")
# Сохраняем в файловый кэш
with open(cache_path, 'wb') as f:
f.write(audio_bytes)
logger.info(f"TTS: done, size={len(audio_bytes)} bytes, cached to {os.path.basename(cache_path)}")
return Response(
content=audio_bytes,
media_type="audio/mpeg",
)
except HTTPException:
raise
except Exception as e:
logger.error(f"TTS error: {str(e)}")
raise HTTPException(500, f"TTS error: {str(e)}")
+141
View File
@@ -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,
}
+106
View File
@@ -0,0 +1,106 @@
from fastapi import APIRouter, Request, HTTPException
from auth import require_auth
from database import get_pool
from config import SUPPORTED_LANGUAGES, ADMIN_IDS
router = APIRouter(prefix="/users", tags=["users"])
SUPPORTED_LEVELS = {"A1", "A2", "B1", "B2", "C1", "C2"}
@router.post("/register")
async def register_user(request: Request):
user_id, username = await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
# Проверяем существует ли пользователь
existing = await conn.fetchrow(
"SELECT id, app_language, current_level FROM users WHERE telegram_id = $1", user_id
)
if existing:
return {
"status": "ok",
"app_language": existing["app_language"],
"current_level": existing["current_level"] or "A1",
"user_id": existing["id"],
"is_admin": user_id in ADMIN_IDS,
}
# Создаем нового пользователя
row = await conn.fetchrow(
"""INSERT INTO users (telegram_id, username, current_level)
VALUES ($1, $2, 'A1')
RETURNING id, app_language, current_level""",
user_id, username or f"id{user_id}",
)
return {
"status": "ok",
"app_language": row["app_language"],
"current_level": row["current_level"],
"user_id": row["id"],
"is_admin": user_id in ADMIN_IDS,
}
@router.get("/me/is-admin")
async def check_is_admin(request: Request):
"""Проверяет, является ли текущий пользователь администратором."""
user_id, _ = await require_auth(request)
return {"is_admin": user_id in ADMIN_IDS}
@router.post("/set-language")
async def set_language(request: Request):
user_id, _ = await require_auth(request)
body = await request.json()
lang = body.get("language", "pl")
if lang not in SUPPORTED_LANGUAGES:
raise HTTPException(400, "Unsupported language")
pool = await get_pool()
async with pool.acquire() as conn:
await conn.execute(
"UPDATE users SET app_language = $1 WHERE telegram_id = $2",
lang, user_id,
)
# 🔧 ИСПРАВЛЕНО: вызов достижений ВНЕ блока async with pool.acquire()
from routers.achievements import check_and_award_achievements
new_achievements = await check_and_award_achievements(user_id, "language_changed", 1)
return {
"status": "ok",
"app_language": lang,
"new_achievements": new_achievements
}
@router.post("/set-level")
async def set_level(request: Request):
"""Устанавливает текущий уровень пользователя (A1, A2, B1, B2, C1, C2)"""
user_id, _ = await require_auth(request)
body = await request.json()
level = body.get("level", "A1")
if level not in SUPPORTED_LEVELS:
raise HTTPException(400, "Unsupported level")
pool = await get_pool()
async with pool.acquire() as conn:
# Проверяем, что пользователь существует
user_exists = await conn.fetchval(
"SELECT 1 FROM users WHERE telegram_id = $1", user_id
)
if not user_exists:
raise HTTPException(401, "User not registered")
await conn.execute(
"UPDATE users SET current_level = $1 WHERE telegram_id = $2",
level, user_id,
)
return {
"status": "ok",
"level": level,
}
+282
View File
@@ -0,0 +1,282 @@
import random
from fastapi import APIRouter, Request, HTTPException
from auth import require_auth
from database import get_pool
router = APIRouter(prefix="/wordquiz", tags=["wordquiz"])
@router.get("/next")
async def get_next_quiz(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"
progress = await conn.fetchrow(
"SELECT words_guessed, recent_words FROM word_quiz_progress WHERE user_id = $1",
telegram_id
)
total_guessed = progress["words_guessed"] if progress else 0
recent_words = list(progress["recent_words"]) if progress and progress["recent_words"] else []
async def pick_word(exclude, respect_exclude=True):
if respect_exclude and exclude:
placeholders = ",".join([f"${i+2}" for i in range(len(exclude))])
return await conn.fetchrow(f"""
SELECT word_pl, translation_ru, translation_uk, translation_en
FROM (
SELECT DISTINCT ON (word_pl) word_pl, translation_ru, translation_uk, translation_en
FROM level_words
WHERE level = $1 AND level_id IS NULL AND word_pl NOT IN ({placeholders})
) AS unique_words
ORDER BY RANDOM()
LIMIT 1
""", current_level, *exclude)
return await conn.fetchrow("""
SELECT word_pl, translation_ru, translation_uk, translation_en
FROM (
SELECT DISTINCT ON (word_pl) word_pl, translation_ru, translation_uk, translation_en
FROM level_words
WHERE level = $1 AND level_id IS NULL
) AS unique_words
ORDER BY RANDOM()
LIMIT 1
""", current_level)
word = await pick_word(recent_words, respect_exclude=True)
if not word:
word = await pick_word([], respect_exclude=False)
if not word:
raise HTTPException(404, "No words found")
correct_polish = word["word_pl"]
translation_field = f"translation_{lang}"
translation = word.get(translation_field) or word.get("translation_en") or correct_polish
# Ищем неправильные варианты
exclude_for_wrong = recent_words + [correct_polish]
placeholders = ",".join([f"${i+3}" for i in range(len(exclude_for_wrong))])
wrong_words = await conn.fetch(f"""
SELECT word_pl
FROM (
SELECT DISTINCT ON (word_pl) word_pl
FROM level_words
WHERE level = $1 AND level_id IS NULL AND word_pl != $2 AND word_pl NOT IN ({placeholders})
) AS unique_wrong
ORDER BY RANDOM()
LIMIT 5
""", current_level, correct_polish, *exclude_for_wrong)
if len(wrong_words) < 2:
wrong_words = await conn.fetch("""
SELECT word_pl
FROM (
SELECT DISTINCT ON (word_pl) word_pl
FROM level_words
WHERE level = $1 AND level_id IS NULL AND word_pl != $2
) AS unique_wrong
ORDER BY RANDOM()
LIMIT 5
""", current_level, correct_polish)
wrong_options = []
seen = {correct_polish.lower()}
for w in wrong_words:
wl = w["word_pl"].lower()
if wl not in seen:
wrong_options.append(w["word_pl"])
seen.add(wl)
if len(wrong_options) >= 2:
break
recent_words = recent_words + [correct_polish] + wrong_options
if len(recent_words) > 45:
recent_words = recent_words[-45:]
await conn.execute("""
INSERT INTO word_quiz_progress (user_id, words_guessed, total_score, recent_words)
VALUES ($1, 0, 0, $2::text[])
ON CONFLICT (user_id) DO UPDATE
SET recent_words = $2::text[]
""", telegram_id, recent_words)
options = wrong_options + [correct_polish]
random.shuffle(options)
if total_guessed <= 10:
difficulty = "easy"
elif total_guessed <= 30:
difficulty = "medium"
elif total_guessed <= 60:
difficulty = "hard"
else:
difficulty = "expert"
return {
"correct_polish": correct_polish,
"translation": translation,
"options": options,
"difficulty": difficulty,
}
@router.post("/check")
async def check_quiz_answer(request: Request):
"""Проверяет ответ, начисляет очки и проверяет достижения"""
telegram_id, _ = await require_auth(request)
body = await request.json()
correct_polish = body.get("correct_polish", "")
user_answer = body.get("answer", "")
mistakes = body.get("mistakes", 0)
completion_time = body.get("completion_time")
if not correct_polish or not user_answer:
raise HTTPException(400, "correct_polish and answer required")
# Защита от читерства: минимальное время 1 секунда
if completion_time is not None and completion_time < 1:
return {
"correct": False,
"correct_streak": 0,
"error": "Too fast. Please take your time."
}
is_correct = user_answer.strip().lower() == correct_polish.strip().lower()
pool = await get_pool()
async with pool.acquire() as conn:
current_streak = await conn.fetchval(
"SELECT correct_streak FROM word_quiz_progress WHERE user_id = $1",
telegram_id
) or 0
if is_correct:
current_streak += 1
else:
current_streak = 0
if is_correct:
score = max(10, 50 - (mistakes * 20))
await conn.execute("""
INSERT INTO word_quiz_progress (user_id, words_guessed, total_score, correct_streak)
VALUES ($1, 1, $2, $3)
ON CONFLICT (user_id) DO UPDATE
SET words_guessed = word_quiz_progress.words_guessed + 1,
total_score = word_quiz_progress.total_score + $2,
correct_streak = $3
""", telegram_id, score, current_streak)
from routers.achievements import check_and_award_achievements
new_achievements = []
total_guessed = await conn.fetchval(
"SELECT words_guessed FROM word_quiz_progress WHERE user_id = $1",
telegram_id
) or 0
ach = await check_and_award_achievements(telegram_id, "quiz_guessed", total_guessed)
new_achievements.extend(ach)
if current_streak >= 10:
ach = await check_and_award_achievements(telegram_id, "quiz_perfect_streak", current_streak)
new_achievements.extend(ach)
if mistakes == 0:
ach = await check_and_award_achievements(telegram_id, "quiz_no_mistakes", 1)
new_achievements.extend(ach)
if completion_time is not None and completion_time > 0 and int(completion_time) <= 3:
ach = await check_and_award_achievements(telegram_id, "quiz_speed", 3)
new_achievements.extend(ach)
wordsearch_score = await conn.fetchval(
"SELECT COALESCE(SUM(score), 0) FROM user_progress WHERE user_id = $1 AND completed_at IS NOT NULL",
telegram_id
) or 0
sentence_score = await conn.fetchval(
"SELECT COALESCE(SUM(score), 0) FROM sentence_progress WHERE user_id = $1 AND completed_at IS NOT NULL",
telegram_id
) or 0
wordquiz_score = await conn.fetchval(
"SELECT COALESCE(total_score, 0) FROM word_quiz_progress WHERE user_id = $1",
telegram_id
) or 0
bubblewords_score = await conn.fetchval(
"SELECT COALESCE(total_score, 0) FROM bubble_words_progress WHERE user_id = $1",
telegram_id
) or 0
grand_total = wordsearch_score + sentence_score + wordquiz_score + bubblewords_score
ach = await check_and_award_achievements(telegram_id, "total_score", grand_total)
new_achievements.extend(ach)
return {
"correct": True,
"score": score,
"correct_streak": current_streak,
"new_achievements": new_achievements,
}
else:
# При 3 ошибках — начисляем утешительные 10 очков
if mistakes >= 3:
await conn.execute("""
INSERT INTO word_quiz_progress (user_id, words_guessed, total_score, correct_streak)
VALUES ($1, 0, 10, 0)
ON CONFLICT (user_id) DO UPDATE
SET total_score = word_quiz_progress.total_score + 10,
correct_streak = 0
""", telegram_id)
else:
await conn.execute("""
INSERT INTO word_quiz_progress (user_id, words_guessed, total_score, correct_streak)
VALUES ($1, 0, 0, $2)
ON CONFLICT (user_id) DO UPDATE
SET correct_streak = $2
""", telegram_id, current_streak)
return {
"correct": False,
"correct_streak": current_streak,
"consolation_score": 10 if mistakes >= 3 else 0,
}
@router.get("/progress")
async def get_quiz_progress(request: Request):
"""Статистика пользователя в квизе"""
telegram_id, _ = await require_auth(request)
pool = await get_pool()
async with pool.acquire() as conn:
progress = await conn.fetchrow(
"SELECT words_guessed, total_score, correct_streak FROM word_quiz_progress WHERE user_id = $1",
telegram_id
)
total_guessed = progress["words_guessed"] if progress else 0
if total_guessed <= 10:
difficulty = "easy"
elif total_guessed <= 30:
difficulty = "medium"
elif total_guessed <= 60:
difficulty = "hard"
else:
difficulty = "expert"
return {
"words_guessed": total_guessed,
"total_score": progress["total_score"] if progress else 0,
"correct_streak": progress["correct_streak"] if progress else 0,
"difficulty": difficulty
}
+201
View File
@@ -0,0 +1,201 @@
polish,english,russian,ukrainian
Cześć jak się masz,Hi how are you,Привет как дела,Привіт як справи
Mam na imię Anna,My name is Anna,Меня зовут Анна,Мене звати Анна
Miło mi cię poznać,Nice to meet you,Приятно познакомиться,Приємно познайомитися
Skąd jesteś,Where are you from,Откуда ты,Звідки ти
Jestem z Polski,I am from Poland,Я из Польши,Я з Польщі
Ile masz lat,How old are you,Сколько тебе лет,Скільки тобі років
Mam trzydzieści cztery lata,I am thirty-four years old,Мне тридцать четыре года,Мені тридцять чотири роки
Dziękuję bardzo,Thank you very much,Большое спасибо,Дуже дякую
Proszę bardzo,You're welcome,Пожалуйста,Будь ласка
Do widzenia,Goodbye,До свидания,До побачення
To jest moja rodzina,This is my family,Это моя семья,Це моя сім'я
Mam brata i siostrę,I have a brother and a sister,У меня есть брат и сестра,У мене є брат і сестра
Moja mama jest lekarzem,My mother is a doctor,Моя мама врач,Моя мама лікарка
Mój tata pracuje w biurze,My father works in an office,Мой папа работает в офисе,Мій тато працює в офісі
Mam dwoje dzieci,I have two children,У меня двое детей,У мене двоє дітей
Moja babcia mieszka w mieście,My grandmother lives in the city,Моя бабушка живёт в городе,Моя бабуся живе в місті
To jest mój mąż,This is my husband,Это мой муж,Це мій чоловік
To jest moja żona,This is my wife,Это моя жена,Це моя дружина
Kocham moją rodzinę,I love my family,Я люблю свою семью,Я люблю свою родину
Mój syn ma pięć lat,My son is five years old,Моему сыну пять лет,Моєму сину п'ять років
Która jest godzina,What time is it,Который час,Котра година
Jest trzecia godzina,It is three o'clock,Сейчас три часа,Зараз третя година
Dzisiaj jest poniedziałek,Today is Monday,Сегодня понедельник,Сьогодні понеділок
Jutro jest wtorek,Tomorrow is Tuesday,Завтра вторник,Завтра вівторок
Mam spotkanie o dziesiątej,I have a meeting at ten,У меня встреча в десять,У мене зустріч о десятій
Wstaję o siódmej rano,I get up at seven in the morning,Я встаю в семь утра,Я встаю о сьомій ранку
Pracuję od poniedziałku do piątku,I work from Monday to Friday,Я работаю с понедельника по пятницу,Я працюю з понеділка до п'ятниці
W sobotę odpoczywam,On Saturday I rest,В субботу я отдыхаю,У суботу я відпочиваю
Ile to kosztuje,How much does this cost,Сколько это стоит,Скільки це коштує
To kosztuje dziesięć złotych,This costs ten zlotys,Это стоит десять злотых,Це коштує десять злотих
Lubię jeść owoce,I like eating fruit,Я люблю есть фрукты,Я люблю їсти фрукти
Chcę pić kawę,I want to drink coffee,Я хочу пить кофе,Я хочу пити каву
Jem śniadanie o ósmej,I eat breakfast at eight,Я завтракаю в восемь,Я снідаю о восьмій
Lubię chleb i ser,I like bread and cheese,Я люблю хлеб и сыр,Я люблю хліб і сир
Nie lubię ryb,I don't like fish,Я не люблю рыбу,Я не люблю рибу
Poproszę wodę,I'd like water please,Дайте пожалуйста воду,Будь ласка дайте воду
Ta zupa jest smaczna,This soup is tasty,Этот суп вкусный,Цей суп смачний
Kupuję warzywa na targu,I buy vegetables at the market,Я покупаю овощи на рынке,Я купую овочі на ринку
On je jabłko,He is eating an apple,Он ест яблоко,Він їсть яблуко
Ona pije herbatę,She is drinking tea,Она пьёт чай,Вона п'є чай
Codziennie chodzę do pracy,Every day I go to work,Каждый день я хожу на работу,Щодня я ходжу на роботу
Rano myję zęby,In the morning I brush my teeth,Утром я чищу зубы,Вранці я чищу зуби
Potem jem śniadanie,Then I eat breakfast,Потом я завтракаю,Потім я снідаю
Jadę autobusem do szkoły,I go to school by bus,Я езжу в школу на автобусе,Я їжджу до школи автобусом
Po pracy wracam do domu,After work I go back home,После работы я возвращаюсь домой,Після роботи я повертаюся додому
Wieczorem oglądam telewizję,In the evening I watch TV,Вечером я смотрю телевизор,Увечері я дивлюся телевізор
Idę spać o jedenastej,I go to sleep at eleven,Я ложусь спать в одиннадцать,Я лягаю спати об одинадцятій
W weekend sprzątam mieszkanie,On the weekend I clean the flat,На выходных я убираю квартиру,На вихідних я прибираю квартиру
Gotuję obiad dla rodziny,I cook dinner for my family,Я готовлю обед для семьи,Я готую обід для родини
Czytam książkę przed snem,I read a book before sleep,Я читаю книгу перед сном,Я читаю книгу перед сном
Dzisiaj jest ciepło,Today it is warm,Сегодня тепло,Сьогодні тепло
Pada deszcz,It is raining,Идёт дождь,Йде дощ
Jutro będzie zimno,Tomorrow it will be cold,Завтра будет холодно,Завтра буде холодно
Świeci słońce,The sun is shining,Светит солнце,Світить сонце
Zimą pada śnieg,In winter it snows,Зимой идёт снег,Взимку йде сніг
Lubię lato bo jest gorąco,I like summer because it is hot,Я люблю лето потому что жарко,Я люблю літо бо спекотно
Wieje silny wiatr,There is a strong wind,Дует сильный ветер,Дме сильний вітер
Jaka jest dzisiaj pogoda,What is the weather like today,Какая сегодня погода,Яка сьогодні погода
Wiosną jest ładna pogoda,In spring the weather is nice,Весной хорошая погода,Навесні гарна погода
Jesienią jest chłodno,In autumn it is cool,Осенью прохладно,Восени прохолодно
Idę do sklepu,I am going to the shop,Я иду в магазин,Я йду до магазину
Chcę kupić nowe buty,I want to buy new shoes,Я хочу купить новую обувь,Я хочу купити нове взуття
Ta koszula jest za droga,This shirt is too expensive,Эта рубашка слишком дорогая,Ця сорочка занадто дорога
Czy macie mniejszy rozmiar,Do you have a smaller size,У вас есть размер поменьше,У вас є менший розмір
Płacę kartą,I pay by card,Я плачу картой,Я плачу карткою
Ile to kosztuje razem,How much is it in total,Сколько это стоит в общем,Скільки це коштує загалом
Ten sklep jest zamknięty,This shop is closed,Этот магазин закрыт,Цей магазин зачинений
Supermarket jest blisko domu,The supermarket is close to home,Супермаркет находится близко к дому,Супермаркет знаходиться близько до дому
Potrzebuję mleka i chleba,I need milk and bread,Мне нужны молоко и хлеб,Мені потрібні молоко і хліб
Ta sukienka jest bardzo ładna,This dress is very pretty,Это платье очень красивое,Ця сукня дуже гарна
Mieszkam w Warszawie,I live in Warsaw,Я живу в Варшаве,Я живу у Варшаві
Szkoła jest niedaleko parku,The school is near the park,Школа находится недалеко от парка,Школа знаходиться недалеко від парку
Biblioteka jest otwarta do szóstej,The library is open until six,Библиотека открыта до шести,Бібліотека відкрита до шостої
W mieście jest dużo sklepów,There are many shops in the city,В городе много магазинов,У місті багато магазинів
Lubię spacerować w parku,I like walking in the park,Я люблю гулять в парке,Я люблю гуляти в парку
Dworzec jest obok szpitala,The station is next to the hospital,Вокзал находится рядом с больницей,Вокзал знаходиться поруч з лікарнею
Jedziemy na wakacje nad morze,We are going on holiday to the sea,Мы едем в отпуск на море,Ми їдемо у відпустку на море
To muzeum jest bardzo stare,This museum is very old,Этот музей очень старый,Цей музей дуже старий
Restauracja jest na rogu ulicy,The restaurant is on the street corner,Ресторан находится на углу улицы,Ресторан знаходиться на розі вулиці
Mój dom jest mały ale ładny,My house is small but nice,Мой дом маленький но красивый,Мій дім маленький але гарний
Jak masz na imię,What is your name,Как тебя зовут,Як тебе звати
Co robisz,What are you doing,Что ты делаешь,Що ти робиш
Gdzie mieszkasz,Where do you live,Где ты живёшь,Де ти живеш
Czy mówisz po angielsku,Do you speak English,Ты говоришь по-английски,Ти розмовляєш англійською
Kim jesteś z zawodu,What is your profession,Кто ты по профессии,Хто ти за професією
Czy lubisz muzykę,Do you like music,Тебе нравится музыка,Тобі подобається музика
Kiedy masz urodziny,When is your birthday,Когда у тебя день рождения,Коли у тебе день народження
Czy masz braci lub siostry,Do you have brothers or sisters,У тебя есть братья или сёстры,У тебе є брати чи сестри
Dlaczego jesteś smutny,Why are you sad,Почему ты грустный,Чому ти сумний
Co lubisz robić w wolnym czasie,What do you like to do in your free time,Что ты любишь делать в свободное время,Що ти любиш робити у вільний час
Moja koszula jest niebieska,My shirt is blue,Моя рубашка синяя,Моя сорочка синя
Ten samochód jest czerwony,This car is red,Эта машина красная,Ця машина червона
Kot jest czarny i biały,The cat is black and white,Кот чёрно-белый,Кіт чорно-білий
Mam zielony plecak,I have a green backpack,У меня зелёный рюкзак,У мене зелений рюкзак
Kwiaty w ogrodzie są żółte,The flowers in the garden are yellow,Цветы в саду жёлтые,Квіти в саду жовті
To jest mój telefon,This is my phone,Это мой телефон,Це мій телефон
Gdzie są moje klucze,Where are my keys,Где мои ключи,Де мої ключі
Na stole leży książka,There is a book on the table,На столе лежит книга,На столі лежить книга
Mam nowy komputer,I have a new computer,У меня новый компьютер,У мене новий комп'ютер
To wszystko na dzisiaj,That's all for today,Это всё на сегодня,Це все на сьогодні
To jest mój pokój,This is my room,Это моя комната,Це моя кімната
W kuchni gotuję obiad,I cook dinner in the kitchen,Я готовлю обед на кухне,Я готую обід на кухні
Łazienka jest obok sypialni,The bathroom is next to the bedroom,Ванная рядом со спальней,Ванна кімната поруч зі спальнею
Mam duży salon,I have a big living room,У меня большая гостиная,У мене велика вітальня
Okno w moim pokoju jest duże,The window in my room is big,Окно в моей комнате большое,Вікно в моїй кімнаті велике
Drzwi są otwarte,The door is open,Дверь открыта,Двері відчинені
W domu mam dwa piętra,My house has two floors,В моём доме два этажа,У моєму домі два поверхи
Lubię siedzieć na balkonie,I like sitting on the balcony,Я люблю сидеть на балконе,Я люблю сидіти на балконі
Ogród jest za domem,The garden is behind the house,Сад находится за домом,Сад знаходиться за будинком
Mieszkanie jest na trzecim piętrze,The flat is on the third floor,Квартира на третьем этаже,Квартира на третьому поверсі
NoszÄ™ czarne buty,I wear black shoes,Я ношу чёрную обувь,Я ношу чорне взуття
Ubieram ciepłą kurtkę zimą,I wear a warm jacket in winter,Зимой я ношу тёплую куртку,Взимку я ношу теплу куртку
Ta czapka jest dla ciebie,This hat is for you,Эта шапка для тебя,Ця шапка для тебе
Mam nową sukienkę na wesele,I have a new dress for the wedding,У меня новое платье на свадьбу,У мене нова сукня на весілля
Te spodnie są za długie,These trousers are too long,Эти брюки слишком длинные,Ці штани занадто довгі
Włóż szalik bo jest zimno,Put on a scarf because it's cold,Надень шарф потому что холодно,Одягни шарф бо холодно
Lubię nosić kolorowe koszule,I like wearing colorful shirts,Я люблю носить цветные рубашки,Я люблю носити кольорові сорочки
Gdzie kupiłeś te rękawiczki,Where did you buy these gloves,Где ты купил эти перчатки,Де ти купив ці рукавички
Moje okulary są na stole,My glasses are on the table,Мои очки на столе,Мої окуляри на столі
Ten sweter jest bardzo miękki,This sweater is very soft,Этот свитер очень мягкий,Цей светр дуже м'який
Idę do szkoły o ósmej,I go to school at eight,Я иду в школу в восемь,Я йду до школи о восьмій
Uczę się matematyki,I study mathematics,Я учу математику,Я вивчаю математику
Mam dzisiaj trzy lekcje,I have three lessons today,Сегодня у меня три урока,Сьогодні в мене три уроки
Nauczycielka jest bardzo miła,The teacher is very nice,Учительница очень добрая,Вчителька дуже добра
Piszę zadanie domowe,I am writing my homework,Я пишу домашнее задание,Я пишу домашнє завдання
W klasie jest dwadzieścia uczniów,There are twenty students in the class,В классе двадцать учеников,У класі двадцять учнів
Lubię przerwę między lekcjami,I like the break between lessons,Мне нравится перерыв между уроками,Мені подобається перерва між уроками
Mam plecak pełen książek,I have a backpack full of books,У меня рюкзак полный книг,У мене рюкзак повний книг
Egzamin jest w piątek,The exam is on Friday,Экзамен в пятницу,Іспит у п'ятницю
Czytamy nową lekturę,We are reading a new book,Мы читаем новую книгу,Ми читаємо нову книгу
Mam psa i kota,I have a dog and a cat,У меня есть собака и кот,У мене є собака і кіт
Pies biega po parku,The dog is running in the park,Собака бегает по парку,Собака бігає по парку
Kot śpi na kanapie,The cat is sleeping on the sofa,Кот спит на диване,Кіт спить на дивані
W zoo widzieliśmy słonia,We saw an elephant at the zoo,В зоопарке мы видели слона,У зоопарку ми бачили слона
Ptaki śpiewają rano,Birds sing in the morning,Птицы поют утром,Птахи співають вранці
Ryby pływają w akwarium,Fish swim in the aquarium,Рыбы плавают в аквариуме,Риби плавають в акваріумі
Konie są bardzo silne,Horses are very strong,Лошади очень сильные,Коні дуже сильні
Mój królik je marchewkę,My rabbit eats a carrot,Мой кролик ест морковь,Мій кролик їсть моркву
Krowy dają mleko,Cows give milk,Коровы дают молоко,Корови дають молоко
Lubię oglądać filmy o zwierzętach,I like watching movies about animals,Я люблю смотреть фильмы о животных,Я люблю дивитися фільми про тварин
Mam długie włosy,I have long hair,У меня длинные волосы,У мене довге волосся
Boli mnie noga,My leg hurts,У меня болит нога,У мене болить нога
Jestem bardzo szczęśliwy,I am very happy,Я очень счастлив,Я дуже щасливий
Ona jest zmęczona po pracy,She is tired after work,Она устала после работы,Вона втомлена після роботи
Czuję się dzisiaj świetnie,I feel great today,Сегодня я чувствую себя отлично,Сьогодні я почуваюся чудово
Jestem trochę zdenerwowany,I am a bit nervous,Я немного нервничаю,Я трохи нервую
Mam duże oczy i krótkie włosy,I have big eyes and short hair,У меня большие глаза и короткие волосы,У мене великі очі і коротке волосся
On jest głodny i chce jeść,He is hungry and wants to eat,Он голоден и хочет есть,Він голодний і хоче їсти
Jestem dumny z mojej pracy,I am proud of my work,Я горжусь своей работой,Я пишаюся своєю роботою
Czasem czuję się samotny,Sometimes I feel lonely,Иногда я чувствую себя одиноким,Іноді я почуваюся самотнім
W niedzielę śpię dłużej,On Sunday I sleep longer,В воскресенье я сплю дольше,У неділю я сплю довше
Biorę prysznic każdego ranka,I take a shower every morning,Я принимаю душ каждое утро,Я приймаю душ щоранку
RobiÄ™ zakupy w sobotÄ™,I do the shopping on Saturday,Я делаю покупки в субботу,Я роблю покупки в суботу
Piszę wiadomość do przyjaciela,I am writing a message to my friend,Я пишу сообщение другу,Я пишу повідомлення другу
Dzwonię do mamy co wieczór,I call my mom every evening,Я звоню маме каждый вечер,Я телефоную мамі щовечора
Słucham muzyki podczas pracy,I listen to music while working,Я слушаю музыку во время работы,Я слухаю музику під час роботи
Ćwiczę jogę rano,I do yoga in the morning,Я занимаюсь йогой утром,Я займаюся йогою вранці
Karmię kota przed pracą,I feed the cat before work,Я кормлю кота перед работой,Я годую кота перед роботою
Podlewam kwiaty co dwa dni,I water the flowers every two days,Я поливаю цветы каждые два дня,Я поливаю квіти кожні два дні
Wychodzę z domu o ósmej,I leave home at eight,Я выхожу из дома в восемь,Я виходжу з дому о восьмій
Jadę do pracy samochodem,I drive to work by car,Я езжу на работу на машине,Я їжджу на роботу машиною
Wolę pociąg niż autobus,I prefer the train over the bus,Я предпочитаю поезд автобусу,Я віддаю перевагу потягу над автобусом
Rower stoi w garażu,The bike is in the garage,Велосипед стоит в гараже,Велосипед стоїть у гаражі
Metro jest szybkie i tanie,The subway is fast and cheap,Метро быстрое и дешёвое,Метро швидке і дешеве
Czekam na przystanku dziesięć minut,I wait at the stop for ten minutes,Я жду на остановке десять минут,Я чекаю на зупинці десять хвилин
Samolot ląduje o północy,The plane lands at midnight,Самолёт приземляется в полночь,Літак приземляється опівночі
Taksówka jest droga ale wygodna,A taxi is expensive but comfortable,Такси дорогое но удобное,Таксі дороге але зручне
Idę pieszo do sklepu,I walk to the shop,Я иду пешком в магазин,Я йду пішки до магазину
Statek płynie do portu,The ship sails to the port,Корабль плывёт в порт,Корабель пливе до порту
Muszę zatankować samochód,I have to refuel the car,Мне нужно заправить машину,Мені потрібно заправити машину
Zadzwonię do ciebie później,I will call you later,Я позвоню тебе позже,Я подзвоню тобі пізніше
Napisz do mnie wiadomość,Write me a message,Напиши мне сообщение,Напиши мені повідомлення
Mój telefon jest wyłączony,My phone is off,Мой телефон выключен,Мій телефон вимкнений
Nie słyszę cię dobrze,I can't hear you well,Я плохо тебя слышу,Я погано тебе чую
Zostaw wiadomość po sygnale,Leave a message after the beep,Оставьте сообщение после сигнала,Залиште повідомлення після сигналу
Czy masz mój numer,Do you have my number,У тебя есть мой номер,Ти маєш мій номер
Rozmawiamy przez internet,We talk over the internet,Мы разговариваем через интернет,Ми розмовляємо через інтернет
Wyślę ci zdjęcie,I will send you a photo,Я пришлю тебе фото,Я надішлю тобі фото
Muszę naładować telefon,I have to charge my phone,Мне нужно зарядить телефон,Мені потрібно зарядити телефон
Zadzwoń jak dojedziesz,Call when you arrive,Позвони когда приедешь,Подзвони коли приїдеш
Lubię słodycze,I like sweets,Я люблю сладости,Я люблю солодощі
Nie lubię wstawać wcześnie,I don't like getting up early,Я не люблю рано вставать,Я не люблю рано вставати
Uwielbiam podróżować,I love traveling,Я обожаю путешествовать,Я обожнюю подорожувати
Nienawidzę czekać w kolejce,I hate waiting in line,Я ненавижу стоять в очереди,Я ненавиджу стояти в черзі
Wolę zimę od lata,I prefer winter to summer,Я предпочитаю зиму лету,Я віддаю перевагу зимі над літом
Bardzo lubię ten kolor,I really like this color,Мне очень нравится этот цвет,Мені дуже подобається цей колір
Nie interesuje mnie polityka,I am not interested in politics,Меня не интересует политика,Мене не цікавить політика
Lubimy razem gotować,We like cooking together,Мы любим готовить вместе,Ми любимо готувати разом
Ona nie lubi hałasu,She doesn't like noise,Она не любит шум,Вона не любить шум
Uwielbiam zapach kawy,I love the smell of coffee,Я обожаю запах кофе,Я обожнюю запах кави
Otwórz okno proszę,Open the window please,Открой окно пожалуйста,Відкрий вікно будь ласка
Zamknij drzwi,Close the door,Закрой дверь,Закрий двері
Usiądź tutaj,Sit here,Сядь здесь,Сядь тут
Poczekaj chwilę,Wait a moment,Подожди немного,Зачекай хвилинку
Chodź ze mną,Come with me,Пойдём со мной,Ходімо зі мною
Nie martw się,Don't worry,Не волнуйся,Не хвилюйся
Wszystko będzie dobrze,Everything will be fine,Всё будет хорошо,Все буде добре
To jest bardzo proste,This is very simple,Это очень просто,Це дуже просто
Masz rację,You are right,Ты прав,Ти маєш рацію
Miłego dnia,Have a nice day,Хорошего дня,Гарного дня
1 polish english russian ukrainian
2 Cześć jak się masz Hi how are you Привет как дела Привіт як справи
3 Mam na imię Anna My name is Anna Меня зовут Анна Мене звати Анна
4 Miło mi cię poznać Nice to meet you Приятно познакомиться Приємно познайомитися
5 Skąd jesteś Where are you from Откуда ты Звідки ти
6 Jestem z Polski I am from Poland Я из Польши Я з Польщі
7 Ile masz lat How old are you Сколько тебе лет Скільки тобі років
8 Mam trzydzieści cztery lata I am thirty-four years old Мне тридцать четыре года Мені тридцять чотири роки
9 Dziękuję bardzo Thank you very much Большое спасибо Дуже дякую
10 Proszę bardzo You're welcome Пожалуйста Будь ласка
11 Do widzenia Goodbye До свидания До побачення
12 To jest moja rodzina This is my family Это моя семья Це моя сім'я
13 Mam brata i siostrę I have a brother and a sister У меня есть брат и сестра У мене є брат і сестра
14 Moja mama jest lekarzem My mother is a doctor Моя мама врач Моя мама лікарка
15 Mój tata pracuje w biurze My father works in an office Мой папа работает в офисе Мій тато працює в офісі
16 Mam dwoje dzieci I have two children У меня двое детей У мене двоє дітей
17 Moja babcia mieszka w mieście My grandmother lives in the city Моя бабушка живёт в городе Моя бабуся живе в місті
18 To jest mój mąż This is my husband Это мой муж Це мій чоловік
19 To jest moja żona This is my wife Это моя жена Це моя дружина
20 Kocham moją rodzinę I love my family Я люблю свою семью Я люблю свою родину
21 Mój syn ma pięć lat My son is five years old Моему сыну пять лет Моєму сину п'ять років
22 Która jest godzina What time is it Который час Котра година
23 Jest trzecia godzina It is three o'clock Сейчас три часа Зараз третя година
24 Dzisiaj jest poniedziałek Today is Monday Сегодня понедельник Сьогодні понеділок
25 Jutro jest wtorek Tomorrow is Tuesday Завтра вторник Завтра вівторок
26 Mam spotkanie o dziesiątej I have a meeting at ten У меня встреча в десять У мене зустріч о десятій
27 Wstaję o siódmej rano I get up at seven in the morning Я встаю в семь утра Я встаю о сьомій ранку
28 Pracuję od poniedziałku do piątku I work from Monday to Friday Я работаю с понедельника по пятницу Я працюю з понеділка до п'ятниці
29 W sobotę odpoczywam On Saturday I rest В субботу я отдыхаю У суботу я відпочиваю
30 Ile to kosztuje How much does this cost Сколько это стоит Скільки це коштує
31 To kosztuje dziesięć złotych This costs ten zlotys Это стоит десять злотых Це коштує десять злотих
32 Lubię jeść owoce I like eating fruit Я люблю есть фрукты Я люблю їсти фрукти
33 Chcę pić kawę I want to drink coffee Я хочу пить кофе Я хочу пити каву
34 Jem śniadanie o ósmej I eat breakfast at eight Я завтракаю в восемь Я снідаю о восьмій
35 Lubię chleb i ser I like bread and cheese Я люблю хлеб и сыр Я люблю хліб і сир
36 Nie lubię ryb I don't like fish Я не люблю рыбу Я не люблю рибу
37 Poproszę wodę I'd like water please Дайте пожалуйста воду Будь ласка дайте воду
38 Ta zupa jest smaczna This soup is tasty Этот суп вкусный Цей суп смачний
39 Kupuję warzywa na targu I buy vegetables at the market Я покупаю овощи на рынке Я купую овочі на ринку
40 On je jabłko He is eating an apple Он ест яблоко Він їсть яблуко
41 Ona pije herbatę She is drinking tea Она пьёт чай Вона п'є чай
42 Codziennie chodzę do pracy Every day I go to work Каждый день я хожу на работу Щодня я ходжу на роботу
43 Rano myję zęby In the morning I brush my teeth Утром я чищу зубы Вранці я чищу зуби
44 Potem jem śniadanie Then I eat breakfast Потом я завтракаю Потім я снідаю
45 Jadę autobusem do szkoły I go to school by bus Я езжу в школу на автобусе Я їжджу до школи автобусом
46 Po pracy wracam do domu After work I go back home После работы я возвращаюсь домой Після роботи я повертаюся додому
47 Wieczorem oglądam telewizję In the evening I watch TV Вечером я смотрю телевизор Увечері я дивлюся телевізор
48 Idę spać o jedenastej I go to sleep at eleven Я ложусь спать в одиннадцать Я лягаю спати об одинадцятій
49 W weekend sprzątam mieszkanie On the weekend I clean the flat На выходных я убираю квартиру На вихідних я прибираю квартиру
50 Gotuję obiad dla rodziny I cook dinner for my family Я готовлю обед для семьи Я готую обід для родини
51 Czytam książkę przed snem I read a book before sleep Я читаю книгу перед сном Я читаю книгу перед сном
52 Dzisiaj jest ciepło Today it is warm Сегодня тепло Сьогодні тепло
53 Pada deszcz It is raining Идёт дождь Йде дощ
54 Jutro będzie zimno Tomorrow it will be cold Завтра будет холодно Завтра буде холодно
55 Świeci słońce The sun is shining Светит солнце Світить сонце
56 Zimą pada śnieg In winter it snows Зимой идёт снег Взимку йде сніг
57 Lubię lato bo jest gorąco I like summer because it is hot Я люблю лето потому что жарко Я люблю літо бо спекотно
58 Wieje silny wiatr There is a strong wind Дует сильный ветер Дме сильний вітер
59 Jaka jest dzisiaj pogoda What is the weather like today Какая сегодня погода Яка сьогодні погода
60 Wiosną jest ładna pogoda In spring the weather is nice Весной хорошая погода Навесні гарна погода
61 Jesienią jest chłodno In autumn it is cool Осенью прохладно Восени прохолодно
62 Idę do sklepu I am going to the shop Я иду в магазин Я йду до магазину
63 Chcę kupić nowe buty I want to buy new shoes Я хочу купить новую обувь Я хочу купити нове взуття
64 Ta koszula jest za droga This shirt is too expensive Эта рубашка слишком дорогая Ця сорочка занадто дорога
65 Czy macie mniejszy rozmiar Do you have a smaller size У вас есть размер поменьше У вас є менший розмір
66 Płacę kartą I pay by card Я плачу картой Я плачу карткою
67 Ile to kosztuje razem How much is it in total Сколько это стоит в общем Скільки це коштує загалом
68 Ten sklep jest zamknięty This shop is closed Этот магазин закрыт Цей магазин зачинений
69 Supermarket jest blisko domu The supermarket is close to home Супермаркет находится близко к дому Супермаркет знаходиться близько до дому
70 Potrzebuję mleka i chleba I need milk and bread Мне нужны молоко и хлеб Мені потрібні молоко і хліб
71 Ta sukienka jest bardzo ładna This dress is very pretty Это платье очень красивое Ця сукня дуже гарна
72 Mieszkam w Warszawie I live in Warsaw Я живу в Варшаве Я живу у Варшаві
73 Szkoła jest niedaleko parku The school is near the park Школа находится недалеко от парка Школа знаходиться недалеко від парку
74 Biblioteka jest otwarta do szóstej The library is open until six Библиотека открыта до шести Бібліотека відкрита до шостої
75 W mieście jest dużo sklepów There are many shops in the city В городе много магазинов У місті багато магазинів
76 Lubię spacerować w parku I like walking in the park Я люблю гулять в парке Я люблю гуляти в парку
77 Dworzec jest obok szpitala The station is next to the hospital Вокзал находится рядом с больницей Вокзал знаходиться поруч з лікарнею
78 Jedziemy na wakacje nad morze We are going on holiday to the sea Мы едем в отпуск на море Ми їдемо у відпустку на море
79 To muzeum jest bardzo stare This museum is very old Этот музей очень старый Цей музей дуже старий
80 Restauracja jest na rogu ulicy The restaurant is on the street corner Ресторан находится на углу улицы Ресторан знаходиться на розі вулиці
81 Mój dom jest mały ale ładny My house is small but nice Мой дом маленький но красивый Мій дім маленький але гарний
82 Jak masz na imię What is your name Как тебя зовут Як тебе звати
83 Co robisz What are you doing Что ты делаешь Що ти робиш
84 Gdzie mieszkasz Where do you live Где ты живёшь Де ти живеш
85 Czy mówisz po angielsku Do you speak English Ты говоришь по-английски Ти розмовляєш англійською
86 Kim jesteś z zawodu What is your profession Кто ты по профессии Хто ти за професією
87 Czy lubisz muzykę Do you like music Тебе нравится музыка Тобі подобається музика
88 Kiedy masz urodziny When is your birthday Когда у тебя день рождения Коли у тебе день народження
89 Czy masz braci lub siostry Do you have brothers or sisters У тебя есть братья или сёстры У тебе є брати чи сестри
90 Dlaczego jesteś smutny Why are you sad Почему ты грустный Чому ти сумний
91 Co lubisz robić w wolnym czasie What do you like to do in your free time Что ты любишь делать в свободное время Що ти любиш робити у вільний час
92 Moja koszula jest niebieska My shirt is blue Моя рубашка синяя Моя сорочка синя
93 Ten samochód jest czerwony This car is red Эта машина красная Ця машина червона
94 Kot jest czarny i biały The cat is black and white Кот чёрно-белый Кіт чорно-білий
95 Mam zielony plecak I have a green backpack У меня зелёный рюкзак У мене зелений рюкзак
96 Kwiaty w ogrodzie są żółte The flowers in the garden are yellow Цветы в саду жёлтые Квіти в саду жовті
97 To jest mój telefon This is my phone Это мой телефон Це мій телефон
98 Gdzie są moje klucze Where are my keys Где мои ключи Де мої ключі
99 Na stole leży książka There is a book on the table На столе лежит книга На столі лежить книга
100 Mam nowy komputer I have a new computer У меня новый компьютер У мене новий комп'ютер
101 To wszystko na dzisiaj That's all for today Это всё на сегодня Це все на сьогодні
102 To jest mój pokój This is my room Это моя комната Це моя кімната
103 W kuchni gotuję obiad I cook dinner in the kitchen Я готовлю обед на кухне Я готую обід на кухні
104 Łazienka jest obok sypialni The bathroom is next to the bedroom Ванная рядом со спальней Ванна кімната поруч зі спальнею
105 Mam duży salon I have a big living room У меня большая гостиная У мене велика вітальня
106 Okno w moim pokoju jest duże The window in my room is big Окно в моей комнате большое Вікно в моїй кімнаті велике
107 Drzwi są otwarte The door is open Дверь открыта Двері відчинені
108 W domu mam dwa piętra My house has two floors В моём доме два этажа У моєму домі два поверхи
109 Lubię siedzieć na balkonie I like sitting on the balcony Я люблю сидеть на балконе Я люблю сидіти на балконі
110 Ogród jest za domem The garden is behind the house Сад находится за домом Сад знаходиться за будинком
111 Mieszkanie jest na trzecim piętrze The flat is on the third floor Квартира на третьем этаже Квартира на третьому поверсі
112 NoszÄ™ czarne buty I wear black shoes Я ношу чёрную обувь Я ношу чорне взуття
113 Ubieram ciepłą kurtkę zimą I wear a warm jacket in winter Зимой я ношу тёплую куртку Взимку я ношу теплу куртку
114 Ta czapka jest dla ciebie This hat is for you Эта шапка для тебя Ця шапка для тебе
115 Mam nową sukienkę na wesele I have a new dress for the wedding У меня новое платье на свадьбу У мене нова сукня на весілля
116 Te spodnie są za długie These trousers are too long Эти брюки слишком длинные Ці штани занадто довгі
117 Włóż szalik bo jest zimno Put on a scarf because it's cold Надень шарф потому что холодно Одягни шарф бо холодно
118 Lubię nosić kolorowe koszule I like wearing colorful shirts Я люблю носить цветные рубашки Я люблю носити кольорові сорочки
119 Gdzie kupiłeś te rękawiczki Where did you buy these gloves Где ты купил эти перчатки Де ти купив ці рукавички
120 Moje okulary są na stole My glasses are on the table Мои очки на столе Мої окуляри на столі
121 Ten sweter jest bardzo miękki This sweater is very soft Этот свитер очень мягкий Цей светр дуже м'який
122 Idę do szkoły o ósmej I go to school at eight Я иду в школу в восемь Я йду до школи о восьмій
123 Uczę się matematyki I study mathematics Я учу математику Я вивчаю математику
124 Mam dzisiaj trzy lekcje I have three lessons today Сегодня у меня три урока Сьогодні в мене три уроки
125 Nauczycielka jest bardzo miła The teacher is very nice Учительница очень добрая Вчителька дуже добра
126 Piszę zadanie domowe I am writing my homework Я пишу домашнее задание Я пишу домашнє завдання
127 W klasie jest dwadzieścia uczniów There are twenty students in the class В классе двадцать учеников У класі двадцять учнів
128 Lubię przerwę między lekcjami I like the break between lessons Мне нравится перерыв между уроками Мені подобається перерва між уроками
129 Mam plecak pełen książek I have a backpack full of books У меня рюкзак полный книг У мене рюкзак повний книг
130 Egzamin jest w piątek The exam is on Friday Экзамен в пятницу Іспит у п'ятницю
131 Czytamy nową lekturę We are reading a new book Мы читаем новую книгу Ми читаємо нову книгу
132 Mam psa i kota I have a dog and a cat У меня есть собака и кот У мене є собака і кіт
133 Pies biega po parku The dog is running in the park Собака бегает по парку Собака бігає по парку
134 Kot śpi na kanapie The cat is sleeping on the sofa Кот спит на диване Кіт спить на дивані
135 W zoo widzieliśmy słonia We saw an elephant at the zoo В зоопарке мы видели слона У зоопарку ми бачили слона
136 Ptaki śpiewają rano Birds sing in the morning Птицы поют утром Птахи співають вранці
137 Ryby pływają w akwarium Fish swim in the aquarium Рыбы плавают в аквариуме Риби плавають в акваріумі
138 Konie są bardzo silne Horses are very strong Лошади очень сильные Коні дуже сильні
139 Mój królik je marchewkę My rabbit eats a carrot Мой кролик ест морковь Мій кролик їсть моркву
140 Krowy dają mleko Cows give milk Коровы дают молоко Корови дають молоко
141 Lubię oglądać filmy o zwierzętach I like watching movies about animals Я люблю смотреть фильмы о животных Я люблю дивитися фільми про тварин
142 Mam długie włosy I have long hair У меня длинные волосы У мене довге волосся
143 Boli mnie noga My leg hurts У меня болит нога У мене болить нога
144 Jestem bardzo szczęśliwy I am very happy Я очень счастлив Я дуже щасливий
145 Ona jest zmęczona po pracy She is tired after work Она устала после работы Вона втомлена після роботи
146 Czuję się dzisiaj świetnie I feel great today Сегодня я чувствую себя отлично Сьогодні я почуваюся чудово
147 Jestem trochę zdenerwowany I am a bit nervous Я немного нервничаю Я трохи нервую
148 Mam duże oczy i krótkie włosy I have big eyes and short hair У меня большие глаза и короткие волосы У мене великі очі і коротке волосся
149 On jest głodny i chce jeść He is hungry and wants to eat Он голоден и хочет есть Він голодний і хоче їсти
150 Jestem dumny z mojej pracy I am proud of my work Я горжусь своей работой Я пишаюся своєю роботою
151 Czasem czuję się samotny Sometimes I feel lonely Иногда я чувствую себя одиноким Іноді я почуваюся самотнім
152 W niedzielę śpię dłużej On Sunday I sleep longer В воскресенье я сплю дольше У неділю я сплю довше
153 Biorę prysznic każdego ranka I take a shower every morning Я принимаю душ каждое утро Я приймаю душ щоранку
154 RobiÄ™ zakupy w sobotÄ™ I do the shopping on Saturday Я делаю покупки в субботу Я роблю покупки в суботу
155 Piszę wiadomość do przyjaciela I am writing a message to my friend Я пишу сообщение другу Я пишу повідомлення другу
156 Dzwonię do mamy co wieczór I call my mom every evening Я звоню маме каждый вечер Я телефоную мамі щовечора
157 Słucham muzyki podczas pracy I listen to music while working Я слушаю музыку во время работы Я слухаю музику під час роботи
158 Ćwiczę jogę rano I do yoga in the morning Я занимаюсь йогой утром Я займаюся йогою вранці
159 Karmię kota przed pracą I feed the cat before work Я кормлю кота перед работой Я годую кота перед роботою
160 Podlewam kwiaty co dwa dni I water the flowers every two days Я поливаю цветы каждые два дня Я поливаю квіти кожні два дні
161 Wychodzę z domu o ósmej I leave home at eight Я выхожу из дома в восемь Я виходжу з дому о восьмій
162 Jadę do pracy samochodem I drive to work by car Я езжу на работу на машине Я їжджу на роботу машиною
163 Wolę pociąg niż autobus I prefer the train over the bus Я предпочитаю поезд автобусу Я віддаю перевагу потягу над автобусом
164 Rower stoi w garażu The bike is in the garage Велосипед стоит в гараже Велосипед стоїть у гаражі
165 Metro jest szybkie i tanie The subway is fast and cheap Метро быстрое и дешёвое Метро швидке і дешеве
166 Czekam na przystanku dziesięć minut I wait at the stop for ten minutes Я жду на остановке десять минут Я чекаю на зупинці десять хвилин
167 Samolot ląduje o północy The plane lands at midnight Самолёт приземляется в полночь Літак приземляється опівночі
168 Taksówka jest droga ale wygodna A taxi is expensive but comfortable Такси дорогое но удобное Таксі дороге але зручне
169 Idę pieszo do sklepu I walk to the shop Я иду пешком в магазин Я йду пішки до магазину
170 Statek płynie do portu The ship sails to the port Корабль плывёт в порт Корабель пливе до порту
171 Muszę zatankować samochód I have to refuel the car Мне нужно заправить машину Мені потрібно заправити машину
172 Zadzwonię do ciebie później I will call you later Я позвоню тебе позже Я подзвоню тобі пізніше
173 Napisz do mnie wiadomość Write me a message Напиши мне сообщение Напиши мені повідомлення
174 Mój telefon jest wyłączony My phone is off Мой телефон выключен Мій телефон вимкнений
175 Nie słyszę cię dobrze I can't hear you well Я плохо тебя слышу Я погано тебе чую
176 Zostaw wiadomość po sygnale Leave a message after the beep Оставьте сообщение после сигнала Залиште повідомлення після сигналу
177 Czy masz mój numer Do you have my number У тебя есть мой номер Ти маєш мій номер
178 Rozmawiamy przez internet We talk over the internet Мы разговариваем через интернет Ми розмовляємо через інтернет
179 Wyślę ci zdjęcie I will send you a photo Я пришлю тебе фото Я надішлю тобі фото
180 Muszę naładować telefon I have to charge my phone Мне нужно зарядить телефон Мені потрібно зарядити телефон
181 Zadzwoń jak dojedziesz Call when you arrive Позвони когда приедешь Подзвони коли приїдеш
182 Lubię słodycze I like sweets Я люблю сладости Я люблю солодощі
183 Nie lubię wstawać wcześnie I don't like getting up early Я не люблю рано вставать Я не люблю рано вставати
184 Uwielbiam podróżować I love traveling Я обожаю путешествовать Я обожнюю подорожувати
185 Nienawidzę czekać w kolejce I hate waiting in line Я ненавижу стоять в очереди Я ненавиджу стояти в черзі
186 Wolę zimę od lata I prefer winter to summer Я предпочитаю зиму лету Я віддаю перевагу зимі над літом
187 Bardzo lubię ten kolor I really like this color Мне очень нравится этот цвет Мені дуже подобається цей колір
188 Nie interesuje mnie polityka I am not interested in politics Меня не интересует политика Мене не цікавить політика
189 Lubimy razem gotować We like cooking together Мы любим готовить вместе Ми любимо готувати разом
190 Ona nie lubi hałasu She doesn't like noise Она не любит шум Вона не любить шум
191 Uwielbiam zapach kawy I love the smell of coffee Я обожаю запах кофе Я обожнюю запах кави
192 Otwórz okno proszę Open the window please Открой окно пожалуйста Відкрий вікно будь ласка
193 Zamknij drzwi Close the door Закрой дверь Закрий двері
194 Usiądź tutaj Sit here Сядь здесь Сядь тут
195 Poczekaj chwilę Wait a moment Подожди немного Зачекай хвилинку
196 Chodź ze mną Come with me Пойдём со мной Ходімо зі мною
197 Nie martw się Don't worry Не волнуйся Не хвилюйся
198 Wszystko będzie dobrze Everything will be fine Всё будет хорошо Все буде добре
199 To jest bardzo proste This is very simple Это очень просто Це дуже просто
200 Masz rację You are right Ты прав Ти маєш рацію
201 Miłego dnia Have a nice day Хорошего дня Гарного дня
+101
View File
@@ -0,0 +1,101 @@
polish,english,russian,ukrainian
Wczoraj byłem w kinie,Yesterday I was at the cinema,Вчера я был в кино,Вчора я був у кіно
Obejrzeliśmy ciekawy film,We watched an interesting movie,Мы посмотрели интересный фильм,Ми подивилися цікавий фільм
W zeszłym roku pojechałem do Włoch,Last year I went to Italy,В прошлом году я ездил в Италию,Минулого року я їздив до Італії
Spotkałam starą przyjaciółkę,I met an old friend,Я встретила старую подругу,Я зустріла стару подругу
Kupiliśmy nowy samochód,We bought a new car,Мы купили новую машину,Ми купили нову машину
Nie mogłem spać całą noc,I couldn't sleep all night,Я не мог спать всю ночь,Я не міг спати всю ніч
Skończyłam studia w zeszłym roku,I finished my studies last year,Я закончила учёбу в прошлом году,Я закінчила навчання минулого року
Zapomniałem klucza w domu,I forgot my key at home,Я забыл ключ дома,Я забув ключ вдома
Rozmawialiśmy o pracy przez godzinę,We talked about work for an hour,Мы разговаривали о работе целый час,Ми розмовляли про роботу цілу годину
Ona urodziła się w Krakowie,She was born in Krakow,Она родилась в Кракове,Вона народилася в Кракові
W przyszłym tygodniu pojadę do rodziny,Next week I will go to my family,На следующей неделе я поеду к семье,Наступного тижня я поїду до родини
Będziemy szukać nowego mieszkania,We will be looking for a new flat,Мы будем искать новую квартиру,Ми будемо шукати нову квартиру
Planuję zmienić pracę,I am planning to change jobs,Я планирую сменить работу,Я планую змінити роботу
Chciałbym nauczyć się nowego języka,I would like to learn a new language,Я хотел бы выучить новый язык,Я хотів би вивчити нову мову
Za miesiąc zaczynam nowy kurs,In a month I am starting a new course,Через месяц я начинаю новый курс,Через місяць я починаю новий курс
Mam nadzieję że pogoda będzie dobra,I hope the weather will be good,Я надеюсь что погода будет хорошей,Я сподіваюся що погода буде гарною
Zamierzam odwiedzić rodziców w niedzielę,I intend to visit my parents on Sunday,Я собираюсь навестить родителей в воскресенье,Я маю намір відвідати батьків у неділю
Będę pracować z domu jutro,I will work from home tomorrow,Завтра я буду работать из дома,Завтра я працюватиму з дому
Chcemy kupić dom za kilka lat,We want to buy a house in a few years,Мы хотим купить дом через несколько лет,Ми хочемо купити будинок за кілька років
Wkrótce zdam egzamin z polskiego,I will soon pass my Polish exam,Скоро я сдам экзамен по польскому,Скоро я складу іспит з польської
Moim zdaniem ten film jest nudny,In my opinion this movie is boring,По-моему этот фильм скучный,На мою думку цей фільм нудний
Wolę herbatę niż kawę,I prefer tea over coffee,Я предпочитаю чай кофе,Я віддаю перевагу чаю над кавою
Uważam że to dobry pomysł,I think it is a good idea,Я считаю что это хорошая идея,Я вважаю що це гарна ідея
Bardziej podoba mi się góra niż morze,I like the mountains more than the sea,Мне больше нравятся горы чем море,Мені більше подобаються гори ніж море
Nie zgadzam się z tobą,I don't agree with you,Я не согласен с тобой,Я не погоджуюся з тобою
Myślę że powinniśmy odpocząć,I think we should rest,Я думаю нам стоит отдохнуть,Я думаю нам варто відпочити
To jest najlepsza restauracja w mieście,This is the best restaurant in the city,Это лучший ресторан в городе,Це найкращий ресторан у місті
Wydaje mi się że masz rację,It seems to me that you are right,Мне кажется что ты прав,Мені здається що ти маєш рацію
Wolałabym zostać w domu,I would rather stay at home,Я бы предпочла остаться дома,Я б краще залишилась вдома
Nie mam nic przeciwko temu,I don't mind that,Я не против этого,Я не проти цього
Musimy zarezerwować bilety na pociąg,We have to book train tickets,Нам нужно забронировать билеты на поезд,Нам потрібно забронювати квитки на потяг
Samolot odlatuje o szóstej rano,The plane departs at six in the morning,Самолёт вылетает в шесть утра,Літак вилітає о шостій ранку
Zgubiliśmy się w nowym mieście,We got lost in the new city,Мы заблудились в новом городе,Ми загубилися в новому місті
Hotel był blisko plaży,The hotel was close to the beach,Отель был близко к пляжу,Готель був близько до пляжу
Musimy przesiąść się na inny autobus,We have to change to a different bus,Нам нужно пересесть на другой автобус,Нам потрібно пересісти на інший автобус
Podróż trwała prawie dziesięć godzin,The journey took almost ten hours,Поездка длилась почти десять часов,Подорож тривала майже десять годин
Czy mogę zobaczyć pana paszport,May I see your passport sir,Могу я увидеть ваш паспорт,Чи можу я побачити ваш паспорт
Zwiedzaliśmy muzeum przez cały dzień,We visited the museum all day,Мы осматривали музей целый день,Ми оглядали музей цілий день
Zapomnieliśmy zabrać mapę,We forgot to bring a map,Мы забыли взять карту,Ми забули взяти карту
To była nasza najlepsza podróż,That was our best trip,Это была наша лучшая поездка,Це була наша найкраща подорож
Boli mnie głowa,I have a headache,У меня болит голова,У мене болить голова
Muszę iść do lekarza,I have to go to the doctor,Мне нужно пойти к врачу,Мені потрібно піти до лікаря
Czuję się dzisiaj lepiej,I feel better today,Сегодня я чувствую себя лучше,Сьогодні я почуваюся краще
Mam katar i kaszel,I have a runny nose and a cough,У меня насморк и кашель,У мене нежить і кашель
Lekarz przepisał mi leki,The doctor prescribed me medicine,Врач выписал мне лекарства,Лікар виписав мені ліки
Powinieneś więcej odpoczywać,You should rest more,Тебе следует больше отдыхать,Тобі слід більше відпочивати
Złamałem rękę podczas gry w piłkę,I broke my arm while playing football,Я сломал руку во время игры в футбол,Я зламав руку під час гри у футбол
Ważne jest jedzenie zdrowej żywności,It is important to eat healthy food,Важно есть здоровую пищу,Важливо їсти здорову їжу
Codziennie biegam żeby być zdrowym,I run every day to be healthy,Я бегаю каждый день чтобы быть здоровым,Я бігаю щодня щоб бути здоровим
Ona źle się czuje od rana,She has felt unwell since the morning,Она плохо себя чувствует с утра,Вона погано почувається з ранку
Pracuję jako nauczyciel w szkole,I work as a teacher at a school,Я работаю учителем в школе,Я працюю вчителем у школі
Mam dużo pracy w tym tygodniu,I have a lot of work this week,У меня много работы на этой неделе,У мене багато роботи цього тижня
Uczę się polskiego od roku,I have been learning Polish for a year,Я учу польский уже год,Я вивчаю польську вже рік
Muszę przygotować raport na jutro,I have to prepare a report for tomorrow,Мне нужно подготовить отчёт на завтра,Мені потрібно підготувати звіт на завтра
Mój szef jest bardzo wymagający,My boss is very demanding,Мой начальник очень требовательный,Мій керівник дуже вимогливий
Studiuję informatykę na uniwersytecie,I study computer science at university,Я изучаю информатику в университете,Я вивчаю інформатику в університеті
Mam egzamin w przyszłym tygodniu,I have an exam next week,У меня экзамен на следующей неделе,У мене іспит наступного тижня
Pracujemy razem nad nowym projektem,We are working together on a new project,Мы вместе работаем над новым проектом,Ми разом працюємо над новим проєктом
Codziennie chodzę na zajęcia,I go to classes every day,Я каждый день хожу на занятия,Я щодня ходжу на заняття
Trudno jest znaleźć dobrą pracę,It is difficult to find a good job,Трудно найти хорошую работу,Важко знайти хорошу роботу
Ten dom jest większy niż tamten,This house is bigger than that one,Этот дом больше чем тот,Цей будинок більший ніж той
Zima jest zimniejsza niż jesień,Winter is colder than autumn,Зима холоднее осени,Зима холодніша за осінь
Moja siostra jest młodsza ode mnie,My sister is younger than me,Моя сестра младше меня,Моя сестра молодша за мене
Ten pociąg jest szybszy niż autobus,This train is faster than the bus,Этот поезд быстрее автобуса,Цей потяг швидший за автобус
To jest najdroższy sklep w mieście,This is the most expensive shop in the city,Это самый дорогой магазин в городе,Це найдорожчий магазин у місті
On mówi lepiej po angielsku niż ja,He speaks English better than me,Он говорит по-английски лучше меня,Він говорить англійською краще за мене
To zadanie jest trudniejsze od poprzedniego,This task is harder than the previous one,Это задание сложнее предыдущего,Це завдання складніше за попереднє
Wolę tę kawiarnię bo jest cichsza,I prefer this café because it is quieter,Я предпочитаю это кафе потому что оно тише,Я віддаю перевагу цій кав'ярні бо вона тихіша
Ten telefon jest tańszy niż tamten,This phone is cheaper than that one,Этот телефон дешевле того,Цей телефон дешевший за той
To był najlepszy dzień w moim życiu,That was the best day of my life,Это был лучший день в моей жизни,Це був найкращий день у моєму житті
W wolnym czasie gram na gitarze,In my free time I play guitar,В свободное время я играю на гитаре,У вільний час я граю на гітарі
Uwielbiam czytać kryminały,I love reading crime novels,Я обожаю читать детективы,Я обожнюю читати детективи
Co weekend jeżdżę na rowerze,Every weekend I ride a bike,Каждые выходные я катаюсь на велосипеде,Кожні вихідні я катаюся на велосипеді
Interesuję się fotografią,I am interested in photography,Я интересуюсь фотографией,Я цікавлюся фотографією
Lubię gotować dla przyjaciół,I like cooking for friends,Я люблю готовить для друзей,Я люблю готувати для друзів
Chodzę na siłownię trzy razy w tygodniu,I go to the gym three times a week,Я хожу в спортзал три раза в неделю,Я ходжу до спортзалу тричі на тиждень
Moim hobby jest malowanie,My hobby is painting,Моё хобби рисование,Моє хобі малювання
Gramy w piłkę nożną w niedziele,We play football on Sundays,Мы играем в футбол по воскресеньям,Ми граємо у футбол по неділях
Ona uwielbia tańczyć,She loves dancing,Она обожает танцевать,Вона обожнює танцювати
Zbieram stare monety,I collect old coins,Я собираю старые монеты,Я збираю старі монети
Przepraszam jak dojść do dworca,Excuse me how do I get to the station,Извините как добраться до вокзала,Перепрошую як дістатися до вокзалу
Proszę iść prosto a potem skręcić w lewo,Please go straight and then turn left,Идите прямо а потом поверните налево,Ідіть прямо а потім поверніть ліворуч
Czy może mi pan pomóc,Can you help me sir,Вы можете мне помочь,Чи можете ви мені допомогти
Nie wiem gdzie jest najbliższy przystanek,I don't know where the nearest stop is,Я не знаю где ближайшая остановка,Я не знаю де найближча зупинка
To jest bardzo daleko stąd,It is very far from here,Это очень далеко отсюда,Це дуже далеко звідси
Czy mógłby pan powtórzyć,Could you repeat sir,Не могли бы вы повторить,Чи могли б ви повторити
Poszukuję apteki w pobliżu,I am looking for a pharmacy nearby,Я ищу аптеку поблизости,Я шукаю аптеку поблизу
Dziękuję za pomoc,Thank you for the help,Спасибо за помощь,Дякую за допомогу
Zgubiłem drogę do hotelu,I lost my way to the hotel,Я заблудился по дороге в отель,Я загубив дорогу до готелю
Ile czasu zajmuje dojazd do centrum,How long does it take to get downtown,Сколько времени занимает дорога до центра,Скільки часу займає дорога до центру
Mój komputer się zepsuł,My computer broke down,Мой компьютер сломался,Мій комп'ютер зламався
Nie mogę znaleźć kluczy,I can't find my keys,Я не могу найти ключи,Я не можу знайти ключі
Zapomniałem hasła do konta,I forgot my account password,Я забыл пароль от аккаунта,Я забув пароль від акаунта
Autobus się spóźnia,The bus is late,Автобус опаздывает,Автобус запізнюється
Skończył mi się czas,I ran out of time,У меня закончилось время,У мене закінчився час
Mam problem z internetem,I have a problem with the internet,У меня проблема с интернетом,У мене проблема з інтернетом
Zepsuła się pralka,The washing machine broke,Стиральная машина сломалась,Пральна машина зламалася
Musimy naprawić dach,We have to fix the roof,Нам нужно починить крышу,Нам потрібно полагодити дах
Sąsiedzi są bardzo głośni,The neighbors are very loud,Соседи очень шумные,Сусіди дуже галасливі
To był naprawdę trudny dzień,That was a really difficult day,Это был действительно трудный день,Це був справді важкий день
1 polish english russian ukrainian
2 Wczoraj byłem w kinie Yesterday I was at the cinema Вчера я был в кино Вчора я був у кіно
3 Obejrzeliśmy ciekawy film We watched an interesting movie Мы посмотрели интересный фильм Ми подивилися цікавий фільм
4 W zeszłym roku pojechałem do Włoch Last year I went to Italy В прошлом году я ездил в Италию Минулого року я їздив до Італії
5 Spotkałam starą przyjaciółkę I met an old friend Я встретила старую подругу Я зустріла стару подругу
6 Kupiliśmy nowy samochód We bought a new car Мы купили новую машину Ми купили нову машину
7 Nie mogłem spać całą noc I couldn't sleep all night Я не мог спать всю ночь Я не міг спати всю ніч
8 Skończyłam studia w zeszłym roku I finished my studies last year Я закончила учёбу в прошлом году Я закінчила навчання минулого року
9 Zapomniałem klucza w domu I forgot my key at home Я забыл ключ дома Я забув ключ вдома
10 Rozmawialiśmy o pracy przez godzinę We talked about work for an hour Мы разговаривали о работе целый час Ми розмовляли про роботу цілу годину
11 Ona urodziła się w Krakowie She was born in Krakow Она родилась в Кракове Вона народилася в Кракові
12 W przyszłym tygodniu pojadę do rodziny Next week I will go to my family На следующей неделе я поеду к семье Наступного тижня я поїду до родини
13 Będziemy szukać nowego mieszkania We will be looking for a new flat Мы будем искать новую квартиру Ми будемо шукати нову квартиру
14 Planuję zmienić pracę I am planning to change jobs Я планирую сменить работу Я планую змінити роботу
15 Chciałbym nauczyć się nowego języka I would like to learn a new language Я хотел бы выучить новый язык Я хотів би вивчити нову мову
16 Za miesiąc zaczynam nowy kurs In a month I am starting a new course Через месяц я начинаю новый курс Через місяць я починаю новий курс
17 Mam nadzieję że pogoda będzie dobra I hope the weather will be good Я надеюсь что погода будет хорошей Я сподіваюся що погода буде гарною
18 Zamierzam odwiedzić rodziców w niedzielę I intend to visit my parents on Sunday Я собираюсь навестить родителей в воскресенье Я маю намір відвідати батьків у неділю
19 Będę pracować z domu jutro I will work from home tomorrow Завтра я буду работать из дома Завтра я працюватиму з дому
20 Chcemy kupić dom za kilka lat We want to buy a house in a few years Мы хотим купить дом через несколько лет Ми хочемо купити будинок за кілька років
21 Wkrótce zdam egzamin z polskiego I will soon pass my Polish exam Скоро я сдам экзамен по польскому Скоро я складу іспит з польської
22 Moim zdaniem ten film jest nudny In my opinion this movie is boring По-моему этот фильм скучный На мою думку цей фільм нудний
23 Wolę herbatę niż kawę I prefer tea over coffee Я предпочитаю чай кофе Я віддаю перевагу чаю над кавою
24 Uważam że to dobry pomysł I think it is a good idea Я считаю что это хорошая идея Я вважаю що це гарна ідея
25 Bardziej podoba mi się góra niż morze I like the mountains more than the sea Мне больше нравятся горы чем море Мені більше подобаються гори ніж море
26 Nie zgadzam się z tobą I don't agree with you Я не согласен с тобой Я не погоджуюся з тобою
27 Myślę że powinniśmy odpocząć I think we should rest Я думаю нам стоит отдохнуть Я думаю нам варто відпочити
28 To jest najlepsza restauracja w mieście This is the best restaurant in the city Это лучший ресторан в городе Це найкращий ресторан у місті
29 Wydaje mi się że masz rację It seems to me that you are right Мне кажется что ты прав Мені здається що ти маєш рацію
30 Wolałabym zostać w domu I would rather stay at home Я бы предпочла остаться дома Я б краще залишилась вдома
31 Nie mam nic przeciwko temu I don't mind that Я не против этого Я не проти цього
32 Musimy zarezerwować bilety na pociąg We have to book train tickets Нам нужно забронировать билеты на поезд Нам потрібно забронювати квитки на потяг
33 Samolot odlatuje o szóstej rano The plane departs at six in the morning Самолёт вылетает в шесть утра Літак вилітає о шостій ранку
34 Zgubiliśmy się w nowym mieście We got lost in the new city Мы заблудились в новом городе Ми загубилися в новому місті
35 Hotel był blisko plaży The hotel was close to the beach Отель был близко к пляжу Готель був близько до пляжу
36 Musimy przesiąść się na inny autobus We have to change to a different bus Нам нужно пересесть на другой автобус Нам потрібно пересісти на інший автобус
37 Podróż trwała prawie dziesięć godzin The journey took almost ten hours Поездка длилась почти десять часов Подорож тривала майже десять годин
38 Czy mogę zobaczyć pana paszport May I see your passport sir Могу я увидеть ваш паспорт Чи можу я побачити ваш паспорт
39 Zwiedzaliśmy muzeum przez cały dzień We visited the museum all day Мы осматривали музей целый день Ми оглядали музей цілий день
40 Zapomnieliśmy zabrać mapę We forgot to bring a map Мы забыли взять карту Ми забули взяти карту
41 To była nasza najlepsza podróż That was our best trip Это была наша лучшая поездка Це була наша найкраща подорож
42 Boli mnie głowa I have a headache У меня болит голова У мене болить голова
43 Muszę iść do lekarza I have to go to the doctor Мне нужно пойти к врачу Мені потрібно піти до лікаря
44 Czuję się dzisiaj lepiej I feel better today Сегодня я чувствую себя лучше Сьогодні я почуваюся краще
45 Mam katar i kaszel I have a runny nose and a cough У меня насморк и кашель У мене нежить і кашель
46 Lekarz przepisał mi leki The doctor prescribed me medicine Врач выписал мне лекарства Лікар виписав мені ліки
47 Powinieneś więcej odpoczywać You should rest more Тебе следует больше отдыхать Тобі слід більше відпочивати
48 Złamałem rękę podczas gry w piłkę I broke my arm while playing football Я сломал руку во время игры в футбол Я зламав руку під час гри у футбол
49 Ważne jest jedzenie zdrowej żywności It is important to eat healthy food Важно есть здоровую пищу Важливо їсти здорову їжу
50 Codziennie biegam żeby być zdrowym I run every day to be healthy Я бегаю каждый день чтобы быть здоровым Я бігаю щодня щоб бути здоровим
51 Ona źle się czuje od rana She has felt unwell since the morning Она плохо себя чувствует с утра Вона погано почувається з ранку
52 Pracuję jako nauczyciel w szkole I work as a teacher at a school Я работаю учителем в школе Я працюю вчителем у школі
53 Mam dużo pracy w tym tygodniu I have a lot of work this week У меня много работы на этой неделе У мене багато роботи цього тижня
54 Uczę się polskiego od roku I have been learning Polish for a year Я учу польский уже год Я вивчаю польську вже рік
55 Muszę przygotować raport na jutro I have to prepare a report for tomorrow Мне нужно подготовить отчёт на завтра Мені потрібно підготувати звіт на завтра
56 Mój szef jest bardzo wymagający My boss is very demanding Мой начальник очень требовательный Мій керівник дуже вимогливий
57 Studiuję informatykę na uniwersytecie I study computer science at university Я изучаю информатику в университете Я вивчаю інформатику в університеті
58 Mam egzamin w przyszłym tygodniu I have an exam next week У меня экзамен на следующей неделе У мене іспит наступного тижня
59 Pracujemy razem nad nowym projektem We are working together on a new project Мы вместе работаем над новым проектом Ми разом працюємо над новим проєктом
60 Codziennie chodzę na zajęcia I go to classes every day Я каждый день хожу на занятия Я щодня ходжу на заняття
61 Trudno jest znaleźć dobrą pracę It is difficult to find a good job Трудно найти хорошую работу Важко знайти хорошу роботу
62 Ten dom jest większy niż tamten This house is bigger than that one Этот дом больше чем тот Цей будинок більший ніж той
63 Zima jest zimniejsza niż jesień Winter is colder than autumn Зима холоднее осени Зима холодніша за осінь
64 Moja siostra jest młodsza ode mnie My sister is younger than me Моя сестра младше меня Моя сестра молодша за мене
65 Ten pociąg jest szybszy niż autobus This train is faster than the bus Этот поезд быстрее автобуса Цей потяг швидший за автобус
66 To jest najdroższy sklep w mieście This is the most expensive shop in the city Это самый дорогой магазин в городе Це найдорожчий магазин у місті
67 On mówi lepiej po angielsku niż ja He speaks English better than me Он говорит по-английски лучше меня Він говорить англійською краще за мене
68 To zadanie jest trudniejsze od poprzedniego This task is harder than the previous one Это задание сложнее предыдущего Це завдання складніше за попереднє
69 Wolę tę kawiarnię bo jest cichsza I prefer this café because it is quieter Я предпочитаю это кафе потому что оно тише Я віддаю перевагу цій кав'ярні бо вона тихіша
70 Ten telefon jest tańszy niż tamten This phone is cheaper than that one Этот телефон дешевле того Цей телефон дешевший за той
71 To był najlepszy dzień w moim życiu That was the best day of my life Это был лучший день в моей жизни Це був найкращий день у моєму житті
72 W wolnym czasie gram na gitarze In my free time I play guitar В свободное время я играю на гитаре У вільний час я граю на гітарі
73 Uwielbiam czytać kryminały I love reading crime novels Я обожаю читать детективы Я обожнюю читати детективи
74 Co weekend jeżdżę na rowerze Every weekend I ride a bike Каждые выходные я катаюсь на велосипеде Кожні вихідні я катаюся на велосипеді
75 Interesuję się fotografią I am interested in photography Я интересуюсь фотографией Я цікавлюся фотографією
76 Lubię gotować dla przyjaciół I like cooking for friends Я люблю готовить для друзей Я люблю готувати для друзів
77 Chodzę na siłownię trzy razy w tygodniu I go to the gym three times a week Я хожу в спортзал три раза в неделю Я ходжу до спортзалу тричі на тиждень
78 Moim hobby jest malowanie My hobby is painting Моё хобби рисование Моє хобі малювання
79 Gramy w piłkę nożną w niedziele We play football on Sundays Мы играем в футбол по воскресеньям Ми граємо у футбол по неділях
80 Ona uwielbia tańczyć She loves dancing Она обожает танцевать Вона обожнює танцювати
81 Zbieram stare monety I collect old coins Я собираю старые монеты Я збираю старі монети
82 Przepraszam jak dojść do dworca Excuse me how do I get to the station Извините как добраться до вокзала Перепрошую як дістатися до вокзалу
83 Proszę iść prosto a potem skręcić w lewo Please go straight and then turn left Идите прямо а потом поверните налево Ідіть прямо а потім поверніть ліворуч
84 Czy może mi pan pomóc Can you help me sir Вы можете мне помочь Чи можете ви мені допомогти
85 Nie wiem gdzie jest najbliższy przystanek I don't know where the nearest stop is Я не знаю где ближайшая остановка Я не знаю де найближча зупинка
86 To jest bardzo daleko stąd It is very far from here Это очень далеко отсюда Це дуже далеко звідси
87 Czy mógłby pan powtórzyć Could you repeat sir Не могли бы вы повторить Чи могли б ви повторити
88 Poszukuję apteki w pobliżu I am looking for a pharmacy nearby Я ищу аптеку поблизости Я шукаю аптеку поблизу
89 Dziękuję za pomoc Thank you for the help Спасибо за помощь Дякую за допомогу
90 Zgubiłem drogę do hotelu I lost my way to the hotel Я заблудился по дороге в отель Я загубив дорогу до готелю
91 Ile czasu zajmuje dojazd do centrum How long does it take to get downtown Сколько времени занимает дорога до центра Скільки часу займає дорога до центру
92 Mój komputer się zepsuł My computer broke down Мой компьютер сломался Мій комп'ютер зламався
93 Nie mogę znaleźć kluczy I can't find my keys Я не могу найти ключи Я не можу знайти ключі
94 Zapomniałem hasła do konta I forgot my account password Я забыл пароль от аккаунта Я забув пароль від акаунта
95 Autobus się spóźnia The bus is late Автобус опаздывает Автобус запізнюється
96 Skończył mi się czas I ran out of time У меня закончилось время У мене закінчився час
97 Mam problem z internetem I have a problem with the internet У меня проблема с интернетом У мене проблема з інтернетом
98 Zepsuła się pralka The washing machine broke Стиральная машина сломалась Пральна машина зламалася
99 Musimy naprawić dach We have to fix the roof Нам нужно починить крышу Нам потрібно полагодити дах
100 Sąsiedzi są bardzo głośni The neighbors are very loud Соседи очень шумные Сусіди дуже галасливі
101 To był naprawdę trudny dzień That was a really difficult day Это был действительно трудный день Це був справді важкий день
+101
View File
@@ -0,0 +1,101 @@
polish,english,russian,ukrainian
Gdybym miał więcej czasu podróżowałbym częściej,If I had more time I would travel more often,Если бы у меня было больше времени я бы путешествовал чаще,Якби я мав більше часу я б подорожував частіше
Gdyby padało zostalibyśmy w domu,If it rained we would stay at home,Если бы шёл дождь мы бы остались дома,Якби йшов дощ ми б залишились удома
Zrobiłbym to inaczej gdybym mógł,I would do it differently if I could,Я бы сделал это по-другому если бы мог,Я б зробив це по-іншому якби міг
Gdybyś mnie zapytał pomógłbym ci,If you had asked me I would have helped you,Если бы ты меня спросил я бы тебе помог,Якби ти мене запитав я б тобі допоміг
Co byś zrobił na moim miejscu,What would you do in my place,Что бы ты сделал на моём месте,Що б ти зробив на моєму місці
Gdyby nie deszcz poszlibyśmy na spacer,If it weren't for the rain we would go for a walk,Если бы не дождь мы бы пошли гулять,Якби не дощ ми б пішли на прогулянку
Chciałbym żeby to było prostsze,I wish it were simpler,Я хотел бы чтобы это было проще,Я хотів би щоб це було простіше
Gdybym wygrał na loterii kupiłbym dom,If I won the lottery I would buy a house,Если бы я выиграл в лотерею я бы купил дом,Якби я виграв у лотерею я б купив будинок
Na twoim miejscu porozmawiałbym z szefem,In your place I would talk to the boss,На твоём месте я бы поговорил с начальником,На твоєму місці я б поговорив з керівником
Gdyby wszyscy się starali świat byłby lepszy,If everyone tried the world would be better,Если бы все старались мир был бы лучше,Якби всі старались світ був би кращим
Uważam że edukacja jest najważniejsza,I believe education is most important,Я считаю что образование важнее всего,Я вважаю що освіта найважливіша
Moim zdaniem warto inwestować w siebie,In my opinion it's worth investing in yourself,По-моему стоит инвестировать в себя,На мою думку варто інвестувати в себе
Zgadzam się z tym stwierdzeniem tylko częściowo,I agree with this statement only partially,Я согласен с этим утверждением лишь частично,Я погоджуюся з цим твердженням лише частково
Z jednej strony to dobry pomysł z drugiej ryzykowny,On one hand it's a good idea on the other it's risky,С одной стороны это хорошая идея с другой рискованная,З одного боку це гарна ідея з іншого ризикована
Wydaje mi się że przesadzasz,It seems to me that you are exaggerating,Мне кажется что ты преувеличиваешь,Мені здається що ти перебільшуєш
Trudno powiedzieć kto ma rację,It's hard to say who is right,Трудно сказать кто прав,Важко сказати хто має рацію
Nie da się tego zrobić bez wsparcia innych,It's impossible to do this without others' support,Это невозможно сделать без поддержки других,Це неможливо зробити без підтримки інших
Według mnie powinniśmy zmienić nasze podejście,In my view we should change our approach,По-моему нам следует изменить наш подход,На мою думку нам слід змінити наш підхід
Argument ten wydaje się dość przekonujący,This argument seems quite convincing,Этот аргумент кажется довольно убедительным,Цей аргумент здається досить переконливим
Nie zawsze zgadzam się z opinią większości,I don't always agree with the majority opinion,Я не всегда согласен с мнением большинства,Я не завжди погоджуюся з думкою більшості
Awansowałem po dwóch latach pracy,I got promoted after two years of work,Я получил повышение через два года работы,Я отримав підвищення через два роки роботи
Zmiana pracy była dla mnie dużym wyzwaniem,Changing jobs was a big challenge for me,Смена работы была для меня большим вызовом,Зміна роботи була для мене великим викликом
Rozważam założenie własnej firmy,I am considering starting my own company,Я подумываю открыть собственную фирму,Я розглядаю можливість відкрити власну фірму
Praca zdalna daje mi więcej wolności,Remote work gives me more freedom,Удалённая работа даёт мне больше свободы,Віддалена робота дає мені більше свободи
Współpraca w zespole nie zawsze jest łatwa,Teamwork is not always easy,Работа в команде не всегда даётся легко,Робота в команді не завжди дається легко
Muszę rozwijać swoje umiejętności zawodowe,I need to develop my professional skills,Мне нужно развивать свои профессиональные навыки,Мені потрібно розвивати свої професійні навички
Wypalenie zawodowe dotyka coraz więcej ludzi,Burnout affects more and more people,Профессиональное выгорание затрагивает всё больше людей,Професійне вигорання торкається дедалі більшої кількості людей
Negocjacje trwały kilka godzin,The negotiations lasted several hours,Переговоры длились несколько часов,Переговори тривали кілька годин
Ostatecznie zdecydowałem się zostać w tej firmie,In the end I decided to stay at this company,В итоге я решил остаться в этой компании,Зрештою я вирішив залишитися в цій компанії
Praca daje mi poczucie spełnienia,Work gives me a sense of fulfillment,Работа даёт мне ощущение самореализации,Робота дає мені відчуття самореалізації
Powinniśmy ograniczyć zużycie plastiku,We should reduce plastic consumption,Нам следует сократить потребление пластика,Нам слід скоротити споживання пластику
Zmiany klimatyczne dotyczą nas wszystkich,Climate change concerns all of us,Изменение климата касается всех нас,Зміна клімату стосується всіх нас
Segreguję śmieci od kilku lat,I have been sorting trash for several years,Я сортирую мусор уже несколько лет,Я сортую сміття вже кілька років
Coraz więcej ludzi rezygnuje z samochodu,More and more people are giving up cars,Всё больше людей отказываются от машины,Дедалі більше людей відмовляються від машини
Zanieczyszczenie powietrza wpływa na nasze zdrowie,Air pollution affects our health,Загрязнение воздуха влияет на наше здоровье,Забруднення повітря впливає на наше здоров'я
Warto sadzić drzewa w miastach,It's worth planting trees in cities,Стоит сажать деревья в городах,Варто садити дерева в містах
Musimy oszczędzać wodę na co dzień,We must save water every day,Нам нужно экономить воду каждый день,Нам потрібно економити воду щодня
Odnawialne źródła energii stają się popularniejsze,Renewable energy sources are becoming more popular,Возобновляемые источники энергии становятся всё популярнее,Відновлювані джерела енергії стають дедалі популярнішими
Ludzie coraz bardziej dbają o środowisko,People are increasingly taking care of the environment,Люди всё больше заботятся об окружающей среде,Люди дедалі більше турбуються про довкілля
To miasto wprowadziło zakaz jednorazowych toreb,This city introduced a ban on single-use bags,Этот город ввёл запрет на одноразовые пакеты,Це місто запровадило заборону на одноразові пакети
Sztuczna inteligencja zmienia nasz sposób pracy,Artificial intelligence is changing the way we work,Искусственный интеллект меняет то как мы работаем,Штучний інтелект змінює те як ми працюємо
Nie wyobrażam sobie życia bez telefonu,I can't imagine life without a phone,Я не представляю жизни без телефона,Я не уявляю життя без телефону
Wiele osób spędza za dużo czasu w internecie,Many people spend too much time online,Многие люди проводят слишком много времени в интернете,Багато людей проводять забагато часу в інтернеті
Ta aplikacja pomaga mi organizować dzień,This app helps me organize my day,Это приложение помогает мне организовать день,Цей додаток допомагає мені організувати день
Technologia rozwija się szybciej niż kiedykolwiek,Technology is developing faster than ever,Технологии развиваются быстрее чем когда-либо,Технології розвиваються швидше ніж будь-коли
Praca zdalna stała się możliwa dzięki internetowi,Remote work became possible thanks to the internet,Удалённая работа стала возможной благодаря интернету,Віддалена робота стала можливою завдяки інтернету
Powinniśmy uważać na to co udostępniamy online,We should be careful about what we share online,Нам следует быть осторожными с тем что мы публикуем в сети,Нам слід бути обережними з тим що ми публікуємо в мережі
Media społecznościowe wpływają na nasze samopoczucie,Social media affects our wellbeing,Социальные сети влияют на наше самочувствие,Соціальні мережі впливають на наше самопочуття
Kupiłem nowy laptop bo stary się zepsuł,I bought a new laptop because the old one broke,Я купил новый ноутбук потому что старый сломался,Я купив новий ноутбук бо старий зламався
Roboty coraz częściej zastępują ludzi w pracy,Robots are increasingly replacing people at work,Роботы всё чаще заменяют людей на работе,Роботи дедалі частіше замінюють людей на роботі
W wiadomościach mówili o nowych przepisach,The news talked about new regulations,В новостях говорили о новых правилах,У новинах говорили про нові правила
Bezrobocie w tym regionie znacznie wzrosło,Unemployment in this region has increased significantly,Безработица в этом регионе значительно выросла,Безробіття в цьому регіоні значно зросло
Wielu ludzi protestowało przeciwko podwyżkom cen,Many people protested against price increases,Многие люди протестовали против повышения цен,Багато людей протестували проти підвищення цін
Rząd ogłosił nowe reformy edukacyjne,The government announced new educational reforms,Правительство объявило о новых реформах образования,Уряд оголосив нові освітні реформи
Nierówności społeczne są coraz bardziej widoczne,Social inequalities are becoming more and more visible,Социальное неравенство становится всё более заметным,Соціальна нерівність стає дедалі помітнішою
Wybory odbędą się w przyszłym miesiącu,The elections will take place next month,Выборы состоятся в следующем месяце,Вибори відбудуться наступного місяця
Media często przesadzają z opisem wydarzeń,The media often exaggerate the description of events,Медиа часто преувеличивают описание событий,Медіа часто перебільшують опис подій
Coraz więcej młodych ludzi emigruje za pracą,More and more young people emigrate for work,Всё больше молодых людей эмигрируют за работой,Дедалі більше молодих людей емігрують за роботою
Społeczeństwo powinno wspierać osoby starsze,Society should support elderly people,Общество должно поддерживать пожилых людей,Суспільство повинно підтримувати літніх людей
Ta decyzja wywołała wiele kontrowersji,This decision caused a lot of controversy,Это решение вызвало много споров,Це рішення викликало багато суперечок
Podróżowanie nauczyło mnie otwartości na inne kultury,Traveling taught me to be open to other cultures,Путешествия научили меня открытости к другим культурам,Подорожі навчили мене відкритості до інших культур
W tym kraju ludzie mają inne zwyczaje niż my,People in this country have different customs than us,В этой стране у людей другие обычаи чем у нас,У цій країні в людей інші звичаї ніж у нас
Byłem zaskoczony gościnnością mieszkańców,I was surprised by the hospitality of the residents,Я был удивлён гостеприимством жителей,Я був здивований гостинністю мешканців
Ta podróż zmieniła moje spojrzenie na życie,This trip changed my outlook on life,Эта поездка изменила мой взгляд на жизнь,Ця подорож змінила мій погляд на життя
Zwiedziliśmy stare miasto pełne zabytków,We visited the old town full of historical monuments,Мы осмотрели старый город полный достопримечательностей,Ми оглянули старе місто повне пам'яток
Lokalna kuchnia bardzo mi się spodobała,I really liked the local cuisine,Мне очень понравилась местная кухня,Мені дуже сподобалась місцева кухня
Warto poznawać tradycje innych narodów,It's worth learning about the traditions of other nations,Стоит узнавать традиции других народов,Варто пізнавати традиції інших народів
Podczas podróży poznałem ludzi z całego świata,During the trip I met people from all over the world,Во время поездки я познакомился с людьми со всего мира,Під час подорожі я познайомився з людьми з усього світу
Ten festiwal przyciąga turystów z całej Europy,This festival attracts tourists from all over Europe,Этот фестиваль привлекает туристов со всей Европы,Цей фестиваль приваблює туристів з усієї Європи
Kultura tego regionu jest bardzo zróżnicowana,The culture of this region is very diverse,Культура этого региона очень разнообразна,Культура цього регіону дуже різноманітна
Przyjaźń wymaga zaufania i cierpliwości,Friendship requires trust and patience,Дружба требует доверия и терпения,Дружба вимагає довіри й терпіння
Rzadko się kłócimy ale gdy tak długo nie rozmawiamy,We rarely argue but when we do we don't talk for a long time,Мы редко ссоримся но когда это случается долго не разговариваем,Ми рідко сваримося але коли так довго не розмовляємо
Trudno jest utrzymać kontakt z dawnymi znajomymi,It's difficult to stay in touch with old acquaintances,Трудно поддерживать связь со старыми знакомыми,Важко підтримувати зв'язок зі старими знайомими
Nasza relacja z czasem stała się silniejsza,Our relationship has become stronger over time,Наши отношения со временем стали крепче,Наші стосунки з часом стали міцнішими
Warto rozmawiać otwarcie o swoich uczuciach,It's worth talking openly about your feelings,Стоит открыто говорить о своих чувствах,Варто відкрито говорити про свої почуття
Czasami trudno zrozumieć punkt widzenia drugiej osoby,Sometimes it's hard to understand another person's point of view,Иногда трудно понять точку зрения другого человека,Іноді важко зрозуміти точку зору іншої людини
Znamy się od dzieciństwa,We have known each other since childhood,Мы знакомы с детства,Ми знайомі з дитинства
Wsparcie rodziny pomogło mi przetrwać trudny czas,My family's support helped me survive a difficult time,Поддержка семьи помогла мне пережить трудное время,Підтримка родини допомогла мені пережити важкий час
Zaufanie buduje się latami a traci w chwilę,Trust is built over years and lost in a moment,Доверие строится годами а теряется в один миг,Довіра будується роками а втрачається за мить
Nie każda znajomość musi przerodzić się w przyjaźń,Not every acquaintance has to turn into a friendship,Не каждое знакомство должно перерасти в дружбу,Не кожне знайомство має перерости в дружбу
Marzę o tym żeby zwiedzić cały świat,I dream of traveling the whole world,Я мечтаю объехать весь мир,Я мрію об'їхати весь світ
Mam nadzieję osiągnąć sukces w tej dziedzinie,I hope to achieve success in this field,Я надеюсь добиться успеха в этой области,Я сподіваюся досягти успіху в цій галузі
Planuję zdobyć nowe kwalifikacje zawodowe,I plan to gain new professional qualifications,Я планирую получить новую профессиональную квалификацию,Я планую здобути нову професійну кваліфікацію
Moim celem jest otworzenie własnej działalności,My goal is to start my own business,Моя цель открыть собственный бизнес,Моя мета відкрити власний бізнес
Chciałbym w przyszłości mieszkać za granicą,I would like to live abroad in the future,В будущем я хотел бы жить за границей,У майбутньому я хотів би жити за кордоном
Staram się realizować swoje marzenia krok po kroku,I try to pursue my dreams step by step,Я стараюсь воплощать свои мечты шаг за шагом,Я намагаюся втілювати свої мрії крок за кроком
Wierzę że ciężka praca się opłaca,I believe that hard work pays off,Я верю что тяжёлый труд окупается,Я вірю що важка праця окупається
Postawiłem sobie ambitny cel na ten rok,I set myself an ambitious goal for this year,Я поставил себе амбициозную цель на этот год,Я поставив собі амбітну мету на цей рік
Nie boję się zmian wręcz przeciwnie ich szukam,I am not afraid of change quite the opposite I seek it,Я не боюсь перемен наоборот ищу их,Я не боюся змін навпаки шукаю їх
Wierzę że za kilka lat będę w innym miejscu w życiu,I believe that in a few years I will be in a different place in life,Я верю что через несколько лет буду в другом месте в жизни,Я вірю що за кілька років буду в іншому місці в житті
Czas płynie szybciej niż nam się wydaje,Time flies faster than we think,Время летит быстрее чем нам кажется,Час минає швидше ніж нам здається
Szczęście nie zawsze zależy od pieniędzy,Happiness doesn't always depend on money,Счастье не всегда зависит от денег,Щастя не завжди залежить від грошей
Każdy z nas popełnia błędy i uczy się na nich,Each of us makes mistakes and learns from them,Каждый из нас совершает ошибки и учится на них,Кожен з нас робить помилки і вчиться на них
Warto doceniać to co się ma,It's worth appreciating what you have,Стоит ценить то что имеешь,Варто цінувати те що маєш
Wolność wiąże się z odpowiedzialnością,Freedom is connected with responsibility,Свобода связана с ответственностью,Свобода пов'язана з відповідальністю
Trudne doświadczenia często czynią nas silniejszymi,Difficult experiences often make us stronger,Трудный опыт часто делает нас сильнее,Важкий досвід часто робить нас сильнішими
Nie zawsze łatwo jest podjąć właściwą decyzję,It's not always easy to make the right decision,Не всегда легко принять правильное решение,Не завжди легко прийняти правильне рішення
Ludzie zmieniają się wraz z upływem czasu,People change as time passes,Люди меняются с течением времени,Люди змінюються з плином часу
Sens życia każdy definiuje inaczej,Everyone defines the meaning of life differently,Каждый по-своему определяет смысл жизни,Кожен по-своєму визначає сенс життя
Warto czasem zatrzymać się i pomyśleć o priorytetach,Sometimes it's worth stopping to think about priorities,Иногда стоит остановиться и подумать о приоритетах,Іноді варто зупинитися і подумати про пріоритети
1 polish english russian ukrainian
2 Gdybym miał więcej czasu podróżowałbym częściej If I had more time I would travel more often Если бы у меня было больше времени я бы путешествовал чаще Якби я мав більше часу я б подорожував частіше
3 Gdyby padało zostalibyśmy w domu If it rained we would stay at home Если бы шёл дождь мы бы остались дома Якби йшов дощ ми б залишились удома
4 Zrobiłbym to inaczej gdybym mógł I would do it differently if I could Я бы сделал это по-другому если бы мог Я б зробив це по-іншому якби міг
5 Gdybyś mnie zapytał pomógłbym ci If you had asked me I would have helped you Если бы ты меня спросил я бы тебе помог Якби ти мене запитав я б тобі допоміг
6 Co byś zrobił na moim miejscu What would you do in my place Что бы ты сделал на моём месте Що б ти зробив на моєму місці
7 Gdyby nie deszcz poszlibyśmy na spacer If it weren't for the rain we would go for a walk Если бы не дождь мы бы пошли гулять Якби не дощ ми б пішли на прогулянку
8 Chciałbym żeby to było prostsze I wish it were simpler Я хотел бы чтобы это было проще Я хотів би щоб це було простіше
9 Gdybym wygrał na loterii kupiłbym dom If I won the lottery I would buy a house Если бы я выиграл в лотерею я бы купил дом Якби я виграв у лотерею я б купив будинок
10 Na twoim miejscu porozmawiałbym z szefem In your place I would talk to the boss На твоём месте я бы поговорил с начальником На твоєму місці я б поговорив з керівником
11 Gdyby wszyscy się starali świat byłby lepszy If everyone tried the world would be better Если бы все старались мир был бы лучше Якби всі старались світ був би кращим
12 Uważam że edukacja jest najważniejsza I believe education is most important Я считаю что образование важнее всего Я вважаю що освіта найважливіша
13 Moim zdaniem warto inwestować w siebie In my opinion it's worth investing in yourself По-моему стоит инвестировать в себя На мою думку варто інвестувати в себе
14 Zgadzam się z tym stwierdzeniem tylko częściowo I agree with this statement only partially Я согласен с этим утверждением лишь частично Я погоджуюся з цим твердженням лише частково
15 Z jednej strony to dobry pomysł z drugiej ryzykowny On one hand it's a good idea on the other it's risky С одной стороны это хорошая идея с другой рискованная З одного боку це гарна ідея з іншого ризикована
16 Wydaje mi się że przesadzasz It seems to me that you are exaggerating Мне кажется что ты преувеличиваешь Мені здається що ти перебільшуєш
17 Trudno powiedzieć kto ma rację It's hard to say who is right Трудно сказать кто прав Важко сказати хто має рацію
18 Nie da się tego zrobić bez wsparcia innych It's impossible to do this without others' support Это невозможно сделать без поддержки других Це неможливо зробити без підтримки інших
19 Według mnie powinniśmy zmienić nasze podejście In my view we should change our approach По-моему нам следует изменить наш подход На мою думку нам слід змінити наш підхід
20 Argument ten wydaje się dość przekonujący This argument seems quite convincing Этот аргумент кажется довольно убедительным Цей аргумент здається досить переконливим
21 Nie zawsze zgadzam się z opinią większości I don't always agree with the majority opinion Я не всегда согласен с мнением большинства Я не завжди погоджуюся з думкою більшості
22 Awansowałem po dwóch latach pracy I got promoted after two years of work Я получил повышение через два года работы Я отримав підвищення через два роки роботи
23 Zmiana pracy była dla mnie dużym wyzwaniem Changing jobs was a big challenge for me Смена работы была для меня большим вызовом Зміна роботи була для мене великим викликом
24 Rozważam założenie własnej firmy I am considering starting my own company Я подумываю открыть собственную фирму Я розглядаю можливість відкрити власну фірму
25 Praca zdalna daje mi więcej wolności Remote work gives me more freedom Удалённая работа даёт мне больше свободы Віддалена робота дає мені більше свободи
26 Współpraca w zespole nie zawsze jest łatwa Teamwork is not always easy Работа в команде не всегда даётся легко Робота в команді не завжди дається легко
27 Muszę rozwijać swoje umiejętności zawodowe I need to develop my professional skills Мне нужно развивать свои профессиональные навыки Мені потрібно розвивати свої професійні навички
28 Wypalenie zawodowe dotyka coraz więcej ludzi Burnout affects more and more people Профессиональное выгорание затрагивает всё больше людей Професійне вигорання торкається дедалі більшої кількості людей
29 Negocjacje trwały kilka godzin The negotiations lasted several hours Переговоры длились несколько часов Переговори тривали кілька годин
30 Ostatecznie zdecydowałem się zostać w tej firmie In the end I decided to stay at this company В итоге я решил остаться в этой компании Зрештою я вирішив залишитися в цій компанії
31 Praca daje mi poczucie spełnienia Work gives me a sense of fulfillment Работа даёт мне ощущение самореализации Робота дає мені відчуття самореалізації
32 Powinniśmy ograniczyć zużycie plastiku We should reduce plastic consumption Нам следует сократить потребление пластика Нам слід скоротити споживання пластику
33 Zmiany klimatyczne dotyczą nas wszystkich Climate change concerns all of us Изменение климата касается всех нас Зміна клімату стосується всіх нас
34 Segreguję śmieci od kilku lat I have been sorting trash for several years Я сортирую мусор уже несколько лет Я сортую сміття вже кілька років
35 Coraz więcej ludzi rezygnuje z samochodu More and more people are giving up cars Всё больше людей отказываются от машины Дедалі більше людей відмовляються від машини
36 Zanieczyszczenie powietrza wpływa na nasze zdrowie Air pollution affects our health Загрязнение воздуха влияет на наше здоровье Забруднення повітря впливає на наше здоров'я
37 Warto sadzić drzewa w miastach It's worth planting trees in cities Стоит сажать деревья в городах Варто садити дерева в містах
38 Musimy oszczędzać wodę na co dzień We must save water every day Нам нужно экономить воду каждый день Нам потрібно економити воду щодня
39 Odnawialne źródła energii stają się popularniejsze Renewable energy sources are becoming more popular Возобновляемые источники энергии становятся всё популярнее Відновлювані джерела енергії стають дедалі популярнішими
40 Ludzie coraz bardziej dbają o środowisko People are increasingly taking care of the environment Люди всё больше заботятся об окружающей среде Люди дедалі більше турбуються про довкілля
41 To miasto wprowadziło zakaz jednorazowych toreb This city introduced a ban on single-use bags Этот город ввёл запрет на одноразовые пакеты Це місто запровадило заборону на одноразові пакети
42 Sztuczna inteligencja zmienia nasz sposób pracy Artificial intelligence is changing the way we work Искусственный интеллект меняет то как мы работаем Штучний інтелект змінює те як ми працюємо
43 Nie wyobrażam sobie życia bez telefonu I can't imagine life without a phone Я не представляю жизни без телефона Я не уявляю життя без телефону
44 Wiele osób spędza za dużo czasu w internecie Many people spend too much time online Многие люди проводят слишком много времени в интернете Багато людей проводять забагато часу в інтернеті
45 Ta aplikacja pomaga mi organizować dzień This app helps me organize my day Это приложение помогает мне организовать день Цей додаток допомагає мені організувати день
46 Technologia rozwija się szybciej niż kiedykolwiek Technology is developing faster than ever Технологии развиваются быстрее чем когда-либо Технології розвиваються швидше ніж будь-коли
47 Praca zdalna stała się możliwa dzięki internetowi Remote work became possible thanks to the internet Удалённая работа стала возможной благодаря интернету Віддалена робота стала можливою завдяки інтернету
48 Powinniśmy uważać na to co udostępniamy online We should be careful about what we share online Нам следует быть осторожными с тем что мы публикуем в сети Нам слід бути обережними з тим що ми публікуємо в мережі
49 Media społecznościowe wpływają na nasze samopoczucie Social media affects our wellbeing Социальные сети влияют на наше самочувствие Соціальні мережі впливають на наше самопочуття
50 Kupiłem nowy laptop bo stary się zepsuł I bought a new laptop because the old one broke Я купил новый ноутбук потому что старый сломался Я купив новий ноутбук бо старий зламався
51 Roboty coraz częściej zastępują ludzi w pracy Robots are increasingly replacing people at work Роботы всё чаще заменяют людей на работе Роботи дедалі частіше замінюють людей на роботі
52 W wiadomościach mówili o nowych przepisach The news talked about new regulations В новостях говорили о новых правилах У новинах говорили про нові правила
53 Bezrobocie w tym regionie znacznie wzrosło Unemployment in this region has increased significantly Безработица в этом регионе значительно выросла Безробіття в цьому регіоні значно зросло
54 Wielu ludzi protestowało przeciwko podwyżkom cen Many people protested against price increases Многие люди протестовали против повышения цен Багато людей протестували проти підвищення цін
55 Rząd ogłosił nowe reformy edukacyjne The government announced new educational reforms Правительство объявило о новых реформах образования Уряд оголосив нові освітні реформи
56 Nierówności społeczne są coraz bardziej widoczne Social inequalities are becoming more and more visible Социальное неравенство становится всё более заметным Соціальна нерівність стає дедалі помітнішою
57 Wybory odbędą się w przyszłym miesiącu The elections will take place next month Выборы состоятся в следующем месяце Вибори відбудуться наступного місяця
58 Media często przesadzają z opisem wydarzeń The media often exaggerate the description of events Медиа часто преувеличивают описание событий Медіа часто перебільшують опис подій
59 Coraz więcej młodych ludzi emigruje za pracą More and more young people emigrate for work Всё больше молодых людей эмигрируют за работой Дедалі більше молодих людей емігрують за роботою
60 Społeczeństwo powinno wspierać osoby starsze Society should support elderly people Общество должно поддерживать пожилых людей Суспільство повинно підтримувати літніх людей
61 Ta decyzja wywołała wiele kontrowersji This decision caused a lot of controversy Это решение вызвало много споров Це рішення викликало багато суперечок
62 Podróżowanie nauczyło mnie otwartości na inne kultury Traveling taught me to be open to other cultures Путешествия научили меня открытости к другим культурам Подорожі навчили мене відкритості до інших культур
63 W tym kraju ludzie mają inne zwyczaje niż my People in this country have different customs than us В этой стране у людей другие обычаи чем у нас У цій країні в людей інші звичаї ніж у нас
64 Byłem zaskoczony gościnnością mieszkańców I was surprised by the hospitality of the residents Я был удивлён гостеприимством жителей Я був здивований гостинністю мешканців
65 Ta podróż zmieniła moje spojrzenie na życie This trip changed my outlook on life Эта поездка изменила мой взгляд на жизнь Ця подорож змінила мій погляд на життя
66 Zwiedziliśmy stare miasto pełne zabytków We visited the old town full of historical monuments Мы осмотрели старый город полный достопримечательностей Ми оглянули старе місто повне пам'яток
67 Lokalna kuchnia bardzo mi się spodobała I really liked the local cuisine Мне очень понравилась местная кухня Мені дуже сподобалась місцева кухня
68 Warto poznawać tradycje innych narodów It's worth learning about the traditions of other nations Стоит узнавать традиции других народов Варто пізнавати традиції інших народів
69 Podczas podróży poznałem ludzi z całego świata During the trip I met people from all over the world Во время поездки я познакомился с людьми со всего мира Під час подорожі я познайомився з людьми з усього світу
70 Ten festiwal przyciąga turystów z całej Europy This festival attracts tourists from all over Europe Этот фестиваль привлекает туристов со всей Европы Цей фестиваль приваблює туристів з усієї Європи
71 Kultura tego regionu jest bardzo zróżnicowana The culture of this region is very diverse Культура этого региона очень разнообразна Культура цього регіону дуже різноманітна
72 Przyjaźń wymaga zaufania i cierpliwości Friendship requires trust and patience Дружба требует доверия и терпения Дружба вимагає довіри й терпіння
73 Rzadko się kłócimy ale gdy tak długo nie rozmawiamy We rarely argue but when we do we don't talk for a long time Мы редко ссоримся но когда это случается долго не разговариваем Ми рідко сваримося але коли так довго не розмовляємо
74 Trudno jest utrzymać kontakt z dawnymi znajomymi It's difficult to stay in touch with old acquaintances Трудно поддерживать связь со старыми знакомыми Важко підтримувати зв'язок зі старими знайомими
75 Nasza relacja z czasem stała się silniejsza Our relationship has become stronger over time Наши отношения со временем стали крепче Наші стосунки з часом стали міцнішими
76 Warto rozmawiać otwarcie o swoich uczuciach It's worth talking openly about your feelings Стоит открыто говорить о своих чувствах Варто відкрито говорити про свої почуття
77 Czasami trudno zrozumieć punkt widzenia drugiej osoby Sometimes it's hard to understand another person's point of view Иногда трудно понять точку зрения другого человека Іноді важко зрозуміти точку зору іншої людини
78 Znamy się od dzieciństwa We have known each other since childhood Мы знакомы с детства Ми знайомі з дитинства
79 Wsparcie rodziny pomogło mi przetrwać trudny czas My family's support helped me survive a difficult time Поддержка семьи помогла мне пережить трудное время Підтримка родини допомогла мені пережити важкий час
80 Zaufanie buduje się latami a traci w chwilę Trust is built over years and lost in a moment Доверие строится годами а теряется в один миг Довіра будується роками а втрачається за мить
81 Nie każda znajomość musi przerodzić się w przyjaźń Not every acquaintance has to turn into a friendship Не каждое знакомство должно перерасти в дружбу Не кожне знайомство має перерости в дружбу
82 Marzę o tym żeby zwiedzić cały świat I dream of traveling the whole world Я мечтаю объехать весь мир Я мрію об'їхати весь світ
83 Mam nadzieję osiągnąć sukces w tej dziedzinie I hope to achieve success in this field Я надеюсь добиться успеха в этой области Я сподіваюся досягти успіху в цій галузі
84 Planuję zdobyć nowe kwalifikacje zawodowe I plan to gain new professional qualifications Я планирую получить новую профессиональную квалификацию Я планую здобути нову професійну кваліфікацію
85 Moim celem jest otworzenie własnej działalności My goal is to start my own business Моя цель открыть собственный бизнес Моя мета відкрити власний бізнес
86 Chciałbym w przyszłości mieszkać za granicą I would like to live abroad in the future В будущем я хотел бы жить за границей У майбутньому я хотів би жити за кордоном
87 Staram się realizować swoje marzenia krok po kroku I try to pursue my dreams step by step Я стараюсь воплощать свои мечты шаг за шагом Я намагаюся втілювати свої мрії крок за кроком
88 Wierzę że ciężka praca się opłaca I believe that hard work pays off Я верю что тяжёлый труд окупается Я вірю що важка праця окупається
89 Postawiłem sobie ambitny cel na ten rok I set myself an ambitious goal for this year Я поставил себе амбициозную цель на этот год Я поставив собі амбітну мету на цей рік
90 Nie boję się zmian wręcz przeciwnie ich szukam I am not afraid of change quite the opposite I seek it Я не боюсь перемен наоборот ищу их Я не боюся змін навпаки шукаю їх
91 Wierzę że za kilka lat będę w innym miejscu w życiu I believe that in a few years I will be in a different place in life Я верю что через несколько лет буду в другом месте в жизни Я вірю що за кілька років буду в іншому місці в житті
92 Czas płynie szybciej niż nam się wydaje Time flies faster than we think Время летит быстрее чем нам кажется Час минає швидше ніж нам здається
93 Szczęście nie zawsze zależy od pieniędzy Happiness doesn't always depend on money Счастье не всегда зависит от денег Щастя не завжди залежить від грошей
94 Każdy z nas popełnia błędy i uczy się na nich Each of us makes mistakes and learns from them Каждый из нас совершает ошибки и учится на них Кожен з нас робить помилки і вчиться на них
95 Warto doceniać to co się ma It's worth appreciating what you have Стоит ценить то что имеешь Варто цінувати те що маєш
96 Wolność wiąże się z odpowiedzialnością Freedom is connected with responsibility Свобода связана с ответственностью Свобода пов'язана з відповідальністю
97 Trudne doświadczenia często czynią nas silniejszymi Difficult experiences often make us stronger Трудный опыт часто делает нас сильнее Важкий досвід часто робить нас сильнішими
98 Nie zawsze łatwo jest podjąć właściwą decyzję It's not always easy to make the right decision Не всегда легко принять правильное решение Не завжди легко прийняти правильне рішення
99 Ludzie zmieniają się wraz z upływem czasu People change as time passes Люди меняются с течением времени Люди змінюються з плином часу
100 Sens życia każdy definiuje inaczej Everyone defines the meaning of life differently Каждый по-своему определяет смысл жизни Кожен по-своєму визначає сенс життя
101 Warto czasem zatrzymać się i pomyśleć o priorytetach Sometimes it's worth stopping to think about priorities Иногда стоит остановиться и подумать о приоритетах Іноді варто зупинитися і подумати про пріоритети
View File
View File
View File
+1
View File
@@ -0,0 +1 @@
"""Tests package for Filwords PL backend."""
+120
View File
@@ -0,0 +1,120 @@
"""
Общие фикстуры для тестов Filwords PL.
Использует реальную БД (dev), но с изолированными тестовыми данными.
После каждого теста данные удаляются.
"""
import pytest
from config import DB_CONFIG
from database import get_pool, init_db
# Тестовые ID — чтобы не пересекаться с реальными данными
TEST_USER_ID = 999999999
TEST_TOPIC_ID = "test_topic_zzz"
@pytest.fixture(scope="session", autouse=True)
async def setup_database():
"""Один раз на сессию: инициализирует БД (миграции)."""
await init_db()
yield
@pytest.fixture
async def conn():
"""Соединение с БД для одного теста."""
pool = await get_pool()
async with pool.acquire() as connection:
yield connection
@pytest.fixture
async def test_user(conn):
"""Создаёт тестового юзера, удаляет после теста."""
await conn.execute("""
INSERT INTO users (telegram_id, username, app_language, current_level)
VALUES ($1, 'test_user', 'ru', 'A2')
ON CONFLICT (telegram_id) DO NOTHING
""", TEST_USER_ID)
yield TEST_USER_ID
# Cleanup — удаляем всё, что связано с юзером.
# ВАЖНО: порядок — сначала дочерние таблицы (FK), потом родительские.
await conn.execute("DELETE FROM user_skill_mastery WHERE user_id = $1", TEST_USER_ID)
await conn.execute("DELETE FROM user_learning_answers WHERE user_id = $1", TEST_USER_ID)
await conn.execute("DELETE FROM user_lesson_progress WHERE user_id = $1", TEST_USER_ID)
await conn.execute("DELETE FROM user_topic_progress WHERE user_id = $1", TEST_USER_ID)
await conn.execute("DELETE FROM user_achievements WHERE user_id = $1", TEST_USER_ID)
await conn.execute("DELETE FROM learning_sessions WHERE user_id = $1", TEST_USER_ID)
await conn.execute("DELETE FROM users WHERE telegram_id = $1", TEST_USER_ID)
@pytest.fixture
async def test_session(conn, test_user):
"""Тестовая сессия обучения (для FK в user_learning_answers)."""
row = await conn.fetchrow("""
INSERT INTO learning_sessions (user_id, level_code, topic_id, session_type)
VALUES ($1, 'A2', 'test_topic', 'lesson')
RETURNING id
""", test_user)
session_id = row["id"]
yield session_id
# 🔧 ФИКС: сначала удаляем ответы, ссылающиеся на сессию (FK),
# только потом саму сессию
await conn.execute("DELETE FROM user_learning_answers WHERE session_id = $1", session_id)
await conn.execute("DELETE FROM learning_sessions WHERE id = $1", session_id)
@pytest.fixture
async def test_topic(conn):
"""Создаёт тестовый топик, удаляет после теста."""
await conn.execute("""
INSERT INTO learning_topics (topic_id, level_code, order_number, name_pl, name_ru, name_uk, name_en)
VALUES ($1, 'A2', 999, 'Test Topic', 'Тестовый топик', 'Тестовий топік', 'Test Topic')
ON CONFLICT (topic_id) DO NOTHING
""", TEST_TOPIC_ID)
yield TEST_TOPIC_ID
await conn.execute("DELETE FROM learning_lessons WHERE topic_id = $1", TEST_TOPIC_ID)
await conn.execute("DELETE FROM learning_topics WHERE topic_id = $1", TEST_TOPIC_ID)
@pytest.fixture
async def create_test_lesson(conn, test_topic):
"""
Фабрика: создаёт урок с произвольным skill_tag.
Использование: await create_test_lesson("test_skill")
"""
created_ids = []
async def _create(skill_tag: str, order: int = 1) -> str:
lesson_id = f"test_lesson_{skill_tag}"
await conn.execute("""
INSERT INTO learning_lessons
(lesson_id, topic_id, order_number, skill_tag,
title_pl, title_ru, title_uk, title_en,
explanation_pl, explanation_ru, explanation_uk, explanation_en)
VALUES ($1, $2, $3, $4,
'Test Lesson', 'Тестовый урок', 'Тестовий урок', 'Test Lesson',
'Test', 'Test', 'Test', 'Test')
ON CONFLICT (lesson_id) DO NOTHING
""", lesson_id, test_topic, order, skill_tag)
created_ids.append(lesson_id)
return lesson_id
yield _create
for lid in created_ids:
await conn.execute("DELETE FROM learning_lessons WHERE lesson_id = $1", lid)
@pytest.fixture
async def clean_mastery(conn, test_user):
"""Очищает user_skill_mastery для тестового юзера до и после теста."""
await conn.execute("DELETE FROM user_skill_mastery WHERE user_id = $1", test_user)
yield
await conn.execute("DELETE FROM user_skill_mastery WHERE user_id = $1", test_user)
+75
View File
@@ -0,0 +1,75 @@
"""
Тесты для check_and_award_achievements.
Проверяют выдачу ачивок без дублей.
"""
import pytest
async def test_achievement_not_awarded_twice(conn, test_user):
"""
Если ачивка уже выдана второй раз не выдаётся.
"""
from routers.achievements import check_and_award_achievements
# Чистим ачивки тестового юзера
await conn.execute("DELETE FROM user_achievements WHERE user_id = $1", test_user)
# Первый раз
result1 = await check_and_award_achievements(test_user, "levels_completed", 1000)
# Второй раз с тем же значением
result2 = await check_and_award_achievements(test_user, "levels_completed", 1000)
# Второй раз должен вернуть пустой список
assert len(result2) == 0
# Cleanup
await conn.execute("DELETE FROM user_achievements WHERE user_id = $1", test_user)
async def test_achievement_awarded_for_matching_value(conn, test_user):
"""
Ачивка выдаётся, если value соответствует требованию.
"""
from routers.achievements import check_and_award_achievements
await conn.execute("DELETE FROM user_achievements WHERE user_id = $1", test_user)
# Уровень 1 завершён — ачивка "first_level" (levels_completed >= 1)
result = await check_and_award_achievements(test_user, "levels_completed", 1)
# Должна быть минимум одна ачивка (first_level)
assert len(result) >= 1
assert any("first" in a["code"].lower() or "level" in a["code"].lower() for a in result)
await conn.execute("DELETE FROM user_achievements WHERE user_id = $1", test_user)
async def test_achievement_returns_localized_name(conn, test_user):
"""
Название ачивки возвращается на языке пользователя (RU).
"""
from routers.achievements import check_and_award_achievements
await conn.execute("DELETE FROM user_achievements WHERE user_id = $1", test_user)
await conn.execute("UPDATE users SET app_language = 'ru' WHERE telegram_id = $1", test_user)
result = await check_and_award_achievements(test_user, "levels_completed", 1)
assert len(result) >= 1
# Название должно быть непустой строкой (RU)
assert result[0]["name"] # не пусто
assert isinstance(result[0]["name"], str)
await conn.execute("DELETE FROM user_achievements WHERE user_id = $1", test_user)
async def test_achievement_unknown_type_returns_empty(conn, test_user):
"""
Неизвестный тип требования пустой список.
"""
from routers.achievements import check_and_award_achievements
result = await check_and_award_achievements(test_user, "nonexistent_type_xyz", 999)
assert result == []
+171
View File
@@ -0,0 +1,171 @@
"""
Тесты для update_skill_mastery.
Проверяют формулы расчёта mastery, retention, trend.
"""
import pytest
from datetime import datetime, timedelta
# ─────────────────────────────────────────────
# ХЕЛПЕР: вставить ответ напрямую в БД
# ─────────────────────────────────────────────
async def insert_answer(
conn,
user_id: int,
session_id: int,
skill_tag: str,
correct: bool,
question_type: str = "recognition",
wrong_because: str | None = None,
days_ago: int = 0,
):
"""Вставляет один ответ в user_learning_answers."""
created_at = datetime.now() - timedelta(days=days_ago)
await conn.execute("""
INSERT INTO user_learning_answers (
user_id, session_id, level_code, topic_id, question_id,
selected_answer, correct, skill_tag, question_type,
context_type, difficulty, time_taken_ms, hint_used,
wrong_because, is_final_answer, created_at
) VALUES (
$1, $2, 'A2', 'test_topic', 'test_q',
'x', $3, $4, $5,
'lesson_practice', 1, 5000, FALSE,
$6, TRUE, $7
)
""", user_id, session_id, correct, skill_tag, question_type, wrong_because, created_at)
# ─────────────────────────────────────────────
# ТЕСТЫ
# ─────────────────────────────────────────────
async def test_mastery_no_answers_returns_none(conn, test_user, clean_mastery):
"""Если у юзера нет ответов по skill_tag — функция возвращает None."""
from routers.learning import update_skill_mastery
result = await update_skill_mastery(conn, test_user, "nonexistent_skill")
assert result is None
async def test_mastery_100_percent_accuracy(conn, test_user, test_session, clean_mastery):
"""Если все ответы правильные — accuracy = 100, mastery = высокая."""
from routers.learning import update_skill_mastery
for _ in range(5):
await insert_answer(conn, test_user, test_session, "test_skill", correct=True)
result = await update_skill_mastery(conn, test_user, "test_skill")
assert result is not None
assert result["attempts"] == 5
assert result["accuracy"] == 100.0
assert result["mastery"] >= 90.0
async def test_mastery_0_percent_accuracy(conn, test_user, test_session, clean_mastery):
"""Если все ответы неправильные — accuracy = 0, mastery = низкая."""
from routers.learning import update_skill_mastery
for _ in range(5):
await insert_answer(conn, test_user, test_session, "test_skill", correct=False, wrong_because="wrong_case")
result = await update_skill_mastery(conn, test_user, "test_skill")
assert result is not None
assert result["accuracy"] == 0.0
assert result["mastery"] <= 30.0
async def test_mastery_50_percent_accuracy(conn, test_user, test_session, clean_mastery):
"""50% accuracy → mastery в среднем диапазоне."""
from routers.learning import update_skill_mastery
for i in range(6):
await insert_answer(conn, test_user, test_session, "test_skill", correct=(i % 2 == 0))
result = await update_skill_mastery(conn, test_user, "test_skill")
assert result is not None
assert 40.0 <= result["accuracy"] <= 60.0
assert 40.0 <= result["mastery"] <= 80.0
async def test_mastery_retention_fresh(conn, test_user, test_session, clean_mastery):
"""Если последний ответ был сегодня — retention_status = 'fresh'."""
from routers.learning import update_skill_mastery
await insert_answer(conn, test_user, test_session, "test_skill", correct=True, days_ago=0)
await insert_answer(conn, test_user, test_session, "test_skill", correct=True, days_ago=0)
result = await update_skill_mastery(conn, test_user, "test_skill")
assert result["retention_status"] == "fresh"
async def test_mastery_retention_needs_review(conn, test_user, test_session, clean_mastery):
"""Если последний ответ 10 дней назад — retention_status = 'needs_review'."""
from routers.learning import update_skill_mastery
await insert_answer(conn, test_user, test_session, "test_skill", correct=True, days_ago=10)
await insert_answer(conn, test_user, test_session, "test_skill", correct=True, days_ago=10)
result = await update_skill_mastery(conn, test_user, "test_skill")
assert result["retention_status"] == "needs_review"
async def test_mastery_retention_overdue(conn, test_user, test_session, clean_mastery):
"""Если последний ответ 20 дней назад — retention_status = 'overdue'."""
from routers.learning import update_skill_mastery
await insert_answer(conn, test_user, test_session, "test_skill", correct=True, days_ago=20)
await insert_answer(conn, test_user, test_session, "test_skill", correct=True, days_ago=20)
result = await update_skill_mastery(conn, test_user, "test_skill")
assert result["retention_status"] == "overdue"
async def test_mastery_trend_improving(conn, test_user, test_session, clean_mastery):
"""Если последние 3 ответа лучше, чем общая точность — trend = 'improving'."""
from routers.learning import update_skill_mastery
# 7 старых неправильных, 3 свежих правильных
for _ in range(7):
await insert_answer(conn, test_user, test_session, "test_skill", correct=False, days_ago=5)
for _ in range(3):
await insert_answer(conn, test_user, test_session, "test_skill", correct=True, days_ago=0)
result = await update_skill_mastery(conn, test_user, "test_skill")
# 3 правильных из 10 = 30% accuracy, но recent_accuracy = 100% → improving
assert result["trend"] == "improving"
async def test_mastery_trend_declining(conn, test_user, test_session, clean_mastery):
"""Если последние 3 ответа хуже, чем общая точность — trend = 'declining'."""
from routers.learning import update_skill_mastery
# 7 старых правильных, 3 свежих неправильных
for _ in range(7):
await insert_answer(conn, test_user, test_session, "test_skill", correct=True, days_ago=5)
for _ in range(3):
await insert_answer(conn, test_user, test_session, "test_skill", correct=False, days_ago=0)
result = await update_skill_mastery(conn, test_user, "test_skill")
assert result["trend"] == "declining"
async def test_mastery_trend_insufficient_data(conn, test_user, test_session, clean_mastery):
"""Если меньше 3 ответов — trend = 'insufficient_data'."""
from routers.learning import update_skill_mastery
await insert_answer(conn, test_user, test_session, "test_skill", correct=True)
await insert_answer(conn, test_user, test_session, "test_skill", correct=True)
result = await update_skill_mastery(conn, test_user, "test_skill")
assert result["trend"] == "insufficient_data"
+99
View File
@@ -0,0 +1,99 @@
"""
Тесты для get_next_lesson.
Проверяют приоритеты: overdue weak test next.
"""
import pytest
from datetime import datetime, timedelta
# ─────────────────────────────────────────────
# ХЕЛПЕРЫ
# ─────────────────────────────────────────────
async def set_user_level(conn, user_id: int, level: str):
await conn.execute("UPDATE users SET current_level = $1 WHERE telegram_id = $2", level, user_id)
async def insert_mastery(
conn,
user_id: int,
skill_tag: str,
mastery: float,
retention: str = "fresh",
attempts: int = 3,
days_since: int = 0,
):
"""Вставляет запись в user_skill_mastery."""
last_attempt = datetime.now() - timedelta(days=days_since)
await conn.execute("""
INSERT INTO user_skill_mastery (
user_id, skill_tag, mastery_score, retention_status,
attempts_count, last_attempt_at
) VALUES ($1, $2, $3, $4, $5, $6)
ON CONFLICT (user_id, skill_tag) DO UPDATE SET
mastery_score = $3,
retention_status = $4,
attempts_count = $5,
last_attempt_at = $6
""", user_id, skill_tag, mastery, retention, attempts, last_attempt)
# ─────────────────────────────────────────────
# ТЕСТЫ
# ─────────────────────────────────────────────
async def test_orchestrator_prioritizes_overdue(conn, test_user, create_test_lesson, clean_mastery):
"""Если есть overdue навык — он выбирается первым."""
from routers.orchestrator import get_next_lesson
await set_user_level(conn, test_user, "A2")
await create_test_lesson("test_skill")
await insert_mastery(conn, test_user, "test_skill", mastery=50.0, retention="overdue", days_since=20)
result = await get_next_lesson(conn, test_user)
assert result["type"] == "review"
assert result["reason"] == "overdue"
async def test_orchestrator_prioritizes_weak(conn, test_user, create_test_lesson, clean_mastery):
"""Если нет overdue, но есть weak (mastery < 75) — выбирается он."""
from routers.orchestrator import get_next_lesson
await set_user_level(conn, test_user, "A2")
await create_test_lesson("test_skill")
await insert_mastery(conn, test_user, "test_skill", mastery=50.0, retention="fresh", days_since=1)
result = await get_next_lesson(conn, test_user)
assert result["type"] == "review"
assert result["reason"] == "weak_mastery"
async def test_orchestrator_prefers_overdue_over_weak(conn, test_user, create_test_lesson, clean_mastery):
"""Если есть и overdue, и weak — overdue важнее."""
from routers.orchestrator import get_next_lesson
await set_user_level(conn, test_user, "A2")
await create_test_lesson("weak_skill", order=1)
await create_test_lesson("overdue_skill", order=2)
await insert_mastery(conn, test_user, "weak_skill", mastery=40.0, retention="fresh", days_since=1)
await insert_mastery(conn, test_user, "overdue_skill", mastery=60.0, retention="overdue", days_since=20)
result = await get_next_lesson(conn, test_user)
assert result["reason"] == "overdue"
async def test_orchestrator_weak_requires_min_attempts(conn, test_user, create_test_lesson, clean_mastery):
"""Weak skill требует минимум 2 попытки — если меньше, он не выбирается."""
from routers.orchestrator import get_next_lesson
await set_user_level(conn, test_user, "A2")
await create_test_lesson("test_skill")
await insert_mastery(conn, test_user, "test_skill", mastery=50.0, retention="fresh", attempts=1)
result = await get_next_lesson(conn, test_user)
assert result["reason"] != "weak_mastery"
+758
View File
@@ -0,0 +1,758 @@
polish,english,russian,ukrainian
adres,address,адрес,адреса
akapit,paragraph,абзац,абзац
akcja,action,действие,дія
aktor,actor,актёр,актор
aktorka,actress,актриса,акторка
aktywność,activity,активность,активність
ale,but,но,але
artykuł,article,статья,стаття
artysta,artist,артист,артист
autobus,bus,автобус,автобус
babcia,grandmother,бабушка,бабуся
banan,banana,банан,банан
bardzo,very,очень,дуже
basen,swimming pool,бассейн,басейн
będę,I will be,я буду,я буду
bez,without,без,без
biały,white,белый,білий
biblioteka,library,библиотека,бібліотека
biedny,poor,бедный,бідний
biegać,to run,бегать,бігати
bilet,ticket,билет,квиток
biurko,desk,письменный стол,письмовий стіл
biuro,office,офис,офіс
biznes,business,бизнес,бізнес
błąd,mistake / error,ошибка,помилка
blisko,close / near,близко,близько
blond,blond / blonde,светловолосый,білявий
bogaty,rich,богатый,багатий
brać,to take,брать,брати
brat,brother,брат,брат
brązowy,brown,коричневый,коричневий
brudny,dirty,грязный,брудний
budować,to build,строить,будувати
budynek,building,здание,будівля
but,shoe,туфля / ботинок,черевик / туфля
butelka,bottle,бутылка,пляшка
cebula,onion,лук,цибуля
cena,price,цена,ціна
centrum,centre / center,центр,центр
chciałbym,I would like,я хотел бы,я хотів би
chcieć,to want,хотеть,хотіти
chleb,bread,хлеб,хліб
chłodny,cool,прохладный,прохолодний
chłopak,boy / boyfriend,парень,хлопець
chłopiec,boy,мальчик,хлопчик
chodzić,to walk / to go regularly,ходить,ходити
chory,sick / ill,больной,хворий
ciąć,to cut,резать,різати
ciało,body,тело,тіло
ciasto,cake,торт,торт
cichy,quiet,тихий,тихий
ciemny,dark,тёмный,темний
ciepły,warm,тёплый,теплий
ciężarówka,truck,грузовик,вантажівка
ciocia,aunt,тётя,тітка
cokolwiek,anything,что угодно,що завгодно
córka,daughter,дочь,донька
coś,something,что-то,щось
cudowny,wonderful,чудесный,чудовий
cukier,sugar,сахар,цукор
ćwiartka,quarter,четверть,чверть
ćwiczenie,exercise,упражнение,вправа
czarny,black,чёрный,чорний
czas,time,время,час
czasami,sometimes,иногда,іноді
czekać,wait,ждать,чекати
czekolada,chocolate,шоколад,шоколад
czerwiec,June,июнь,червень
czerwony,red,красный,червоний
cześć,hello; hi,привет,привіт
część,part; piece,часть,частина
często,often,часто,часто
czterdzieści,forty,сорок,сорок
czternaście,fourteen,четырнадцать,чотирнадцять
cztery,four,четыре,чотири
czujny,alert; vigilant,бдительный,пильний
Czwartek,Thursday,четверг,четвер
czwarty,fourth,четвёртый,четвертий
czysty,clean,чистый,чистий
czytać,read,читать,читати
czytanie,reading,чтение,читання
czytelnik,reader,читатель,читач
dać,give,дать,дати
daleko,far,далеко,далеко
darmowy,free,бесплатный,безкоштовний
decydować,decide,решать,вирішувати
deszcz,rain,дождь,дощ
dialog,dialogue,диалог,діалог
dieta,diet,диета,дієта
dla,for,для,для
dlaczego,why,почему,чому
długi,long,длинный,довгий
długopis,pen,ручка,ручка
do,to; until,до,до
dobry,good,хороший,добрий
dobrze,well; okay,хорошо,добре
dodać,add,добавить,додати
dodatkowy,additional,дополнительный,додатковий
dołączyć,join,присоединиться,приєднатися
dom,house; home,дом,дім
dorosły,adult,взрослый,дорослий
dość,enough,достаточно,достатньо
doskonały,excellent; perfect,отличный; идеальный,відмінний; ідеальний
dostać,get; receive,получить,отримати
dowolny,any,любой,будь-який
droga,road; way,дорога; путь,дорога; шлях
drogi,expensive; dear,дорогой,дорогий
drużyna,team,команда,команда
drzewo,tree,дерево,дерево
drzwi,door,дверь,двері
dużo,a lot; much; many,много,багато
duży,big; large,большой,великий
dwa,two,два,два
dwadzieścia,twenty,двадцать,двадцять
dwanaście,twelve,двенадцать,дванадцять
dyskutować,discuss,обсуждать,обговорювати
dziadek,grandfather,дедушка,дідусь
dziecko,child,ребёнок,дитина
dziękować,thank,благодарить,дякувати
dziękuję,thank you,спасибо,дякую
dzielić,divide; share,делить; делиться,ділити; ділитися
dzień,day,день,день
dziesięć,ten,десять,десять
dziewczyna,girl,девушка; девочка,дівчина; дівчинка
dziewięć,nine,девять,дев'ять
dziewięćdziesiąt,ninety,девяносто,дев'яносто
dziewiętnaście,nineteen,девятнадцать,дев'ятнадцять
dżinsy,jeans,джинсы,джинси
dziś,today,сегодня,сьогодні
dźwięk,sound,звук,звук
egzamin,exam,экзамен,іспит
ekscytujący,exciting,захватывающий,захопливий
fakt,fact,факт,факт
fałszywy,false; fake,ложный; поддельный,хибний; підроблений
fantastyczny,fantastic,фантастический; замечательный,фантастичний; чудовий
farba,paint,краска,фарба
farma,farm,ферма,ферма
festiwal,festival,фестиваль,фестиваль
film,film; movie,фильм,фільм
finałowy,final,финальный,фінальний
fioletowy,purple,фиолетовый,фіолетовий
firma,company; firm,компания,компанія
forma,form; shape,форма,форма
fortepian,piano,фортепиано,фортепіано
fotografia,photograph; photography,фотография; фотография как искусство,фотографія; фотографія як мистецтво
fraza,phrase,фраза,фраза
funt,pound,фунт,фунт
gazeta,newspaper,газета,газета
gdzie,where,где,де
geografia,geography,география,географія
gitara,guitar,гитара,гітара
głodny,hungry,голодный,голодний
głowa,head,голова,голова
główny,main; main/primary,главный,головний
godzina,hour; o'clock,час,година
góra,mountain; hill,гора; холм,гора; пагорб
gorący,hot,горячий,гарячий
gotować,to cook,готовить,готувати
gotowanie,cooking,готовка; приготовление пищи,готування
gotowy,ready,готовый,готовий
gra,game; play,игра,гра
grać,to play,играть,грати
gracz,player,игрок,гравець
gruby,fat; thick,толстый,товстий
grudzień,December,декабрь,грудень
grupa,group,группа,група
gwiazda,star,звезда,зірка
hej,hey; hi,эй; привет,гей; привіт
herbata,tea,чай,чай
historia,history; story,история; рассказ,історія; розповідь
hobby,hobby,хобби,хобі
hotel,hotel,отель; гостиница,готель
ich,their,их,їхній
imię,name; first name,имя,ім'я
impreza,party; event,вечеринка; мероприятие,вечірка; захід
informacja,information,информация,інформація
inny,other; different,другой; иной,інший
inteligentny,intelligent; smart,умный; интеллектуальный,розумний; інтелектуальний
interesujący,interesting,интересный,цікавий
internet,internet,интернет,інтернет
iść,to go; to walk,идти,йти
,her,её,її
jabłko,apple,яблоко,яблуко
jajko,egg,яйцо,яйце
jak,how; like,как,як
jeden,one; one (masculine),один,один
jedenaście,eleven,одиннадцать,одинадцять
jednak,however; nevertheless,однако; всё же,однак; все ж
jedzenie,food; eating,еда; питание,їжа; харчування
jego,his,его,його
jeść,to eat,есть,їсти
jeśli,if,если,якщо
jeździć,to travel; to ride; to go regularly,ездить; путешествовать,їздити; подорожувати
język,language; tongue,язык,мова; язик
jutro,tomorrow,завтра,завтра
kanapka,sandwich,бутерброд; сэндвич,бутерброд; сендвіч
kapelusz,hat,шляпа,капелюх
kąpiel,bath; bathing,ванна; купание,ванна; купання
kariera,career,карьера,кар'єра
karta,card; menu,карта; карточка; меню,картка; меню
kawa,coffee,кофе,кава
kawał,piece; bit,кусок; немного,шматок; трохи
kawiarnia,café; coffee shop,кафе,кав'ярня; кафе
każdy,every; each; everyone,каждый,кожен
kelner,waiter,официант,офіціант
kiedy,when,когда,коли
kiedykolwiek,ever; at any time,когда-либо,коли-небудь
kierowca,driver,водитель,водій
kilka,several; a few,несколько,кілька
klasa,class,класс,клас
klub,club,клуб,клуб
klucz,key,ключ,ключ
kobieta,woman,женщина,жінка
kochać,to love,любить,кохати; любити
kolacja,dinner; supper,ужин,вечеря
kolor,color,цвет,колір
komputer,computer,компьютер,комп'ютер
koń,horse,лошадь,кінь
koncert,concert,концерт,концерт
koniec,end,конец,кінець
koszt,cost,стоимость; цена,вартість; ціна
koszula,shirt,рубашка,сорочка
koszykówka,basketball,баскетбол,баскетбол
kot,cat,кот; кошка,кіт; кішка
kraj,country; land,страна; государство,країна; держава
krótki,short,короткий,короткий
krowa,cow,корова,корова
krzesło,chair,стул,стілець
książka,book,книга,книга
kto,who,кто,хто
który,which; which one,который; какой,який; котрий
ktoś,someone; somebody,кто-то; кто-нибудь,хтось; хтось
kubek,cup; mug,чашка,чашка
kuchnia,kitchen; cuisine,кухня; кухня (национальная),кухня
kultura,culture,культура,культура
kupić,to buy,купить,купити
kurczak,chicken,курица; цыплёнок,курка; курча
kurs,course; course of study,курс,курс
kurtka,jacket,куртка,куртка
kuzyn,cousin (male),двоюродный брат; кузен,двоюрідний брат; кузен
kwiat,flower,цветок,квітка
kwiecień,April,апрель,квітень
ładny,pretty; nice,красивый; симпатичный,гарний; симпатичний
lato,summer,лето,літо
łatwy,easy,лёгкий,легкий
łazienka,bathroom,ванная комната,ванна кімната
lekarz,doctor,врач,лікар
lekcja,lesson,урок,урок
lepszy,better,лучший; лучше,кращий; краще
lew,lion,лев,лев
leżeć,to lie; to be lying,лежать,лежати
licznik,meter; counter,счётчик; измерительный прибор,лічильник; вимірювальний прилад
linia,line,линия; линия,лінія
lipiec,July,июль,липень
list,letter,письмо,лист
lista,list,список,список
listopad,November,ноябрь,листопад
literować,to spell,произносить по буквам,називати по літерах
lód,ice,лёд,лід
lody,ice cream,мороженое,морозиво
łódź,boat,лодка,човен
lokalny,local,местный,місцевий
lot,flight,полёт; рейс,політ; рейс
lotnisko,airport,аэропорт,аеропорт
łóżko,bed,кровать,ліжко
lub,or,или,або
lubić,to like,любить; нравиться,любити; подобатися
ludzie,people,люди,люди
lunch,lunch,обед; ланч,обід; ланч
luty,February,февраль,лютий
magazyn,magazine; warehouse,журнал; склад,журнал; склад
maj,May,май,травень
mały,small; little,маленький,малий
mama,mum; mother,мама,мама
mapa,map,карта,карта
marchew,carrot,морковь,морква
marzec,March,март,березень
masło,butter,сливочное масло,вершкове масло
maszyna,machine,машина; механизм,машина; механізм
matka,mother,мать,мати
mąż,husband,муж,чоловік
menu,menu,меню,меню
mężczyzna,man,мужчина,чоловік
miasto,city; town,город,місто
mieć,to have,иметь,мати
miejsce,place; seat,место,місце
miesiąc,month,месяц,місяць
mięso,meat,мясо,м'ясо
mieszkanie,flat; apartment,квартира,квартира
mila,mile,миля,миля
milion,million,миллион,мільйон
miły,nice; kind,милый; добрый,милий; добрий
minuta,minute,минута,хвилина
mleko,milk,молоко,молоко
młody,young,молодой,молодий
mnie,me,меня; мне,мене; мені
móc,to be able to; can,мочь,могти
model,model,модель,модель
mógł,could; was able to,мог,міг
mój,my,мой,мій
moment,moment,момент,момент
mówić,to speak; to say,говорить,говорити
może,maybe; perhaps,может быть; возможно,можливо; мабуть
możliwy,possible,возможный,можливий
musieć,to have to; must,быть вынужденным; должен,мусити; бути змушеним
muzeum,museum,музей,музей
muzyka,music,музыка,музика
my,we,мы,ми
myć,to wash,мыть,мити
myśleć,to think,думать,думати
nad,over; above; by,над; выше; у,над; біля
najlepszy,best,лучший,найкращий
najpierw,first; firstly,сначала; сперва,спочатку
najwięcej,the most,больше всего; наиболее,найбільше
napełniać,to fill,наполнять,наповнювати
naprawdę,really,действительно; правда,справді; насправді
nas,us,нас,нас
następny,next,следующий,наступний
nasz,our,наш,наш
naturalny,natural,естественный; натуральный,природний; натуральний
nauczyciel,teacher,учитель,учитель
nauka,learning; science,учёба; наука,навчання; наука
naukowiec,scientist,учёный,науковець
nawet,even,даже,навіть
negatywny,negative,негативный,негативний
nic,nothing,ничего; ничто,нічого; ніщо
nie,no; not,не; нет,не; ні
niebezpieczny,dangerous,опасный,небезпечний
niebieski,blue,синий; голубой,синій; блакитний
niedziela,Sunday,воскресенье,неділя
niektóre,some; certain,некоторые,деякі
niemowlę,baby; infant,младенец,немовля
nienawidzić,to hate,ненавидеть,ненавидіти
niesamowity,amazing; incredible,невероятный; потрясающий,неймовірний; дивовижний
nigdy,never,никогда,ніколи
nikt,nobody; no one,никто,ніхто
niż,than,чем; нежели,ніж
noc,night,ночь,ніч
noga,leg; foot,нога,нога
nos,nose,нос,ніс
nosić,to wear; to carry,носить,носити
notatka,note,заметка; записка,нотатка; записка
nowoczesny,modern,современный,сучасний
nowy,new,новый,новий
nudny,boring,скучный,нудний
numer,number,номер; число,номер; число
obiekt,object; facility,объект,об'єкт
obok,next to; beside,рядом; возле,поруч; біля
obraz,picture; image,картина; изображение,картина; зображення
obszar,area,область; район,область; район
ocean,ocean,океан,океан
odpowiedzieć,to answer; to reply,ответить,відповісти
odwiedzać,to visit,посещать; навещать,відвідувати
odwiedzający,visitor,посетитель,відвідувач
odwrotny,opposite; reverse,обратный; противоположный,зворотний; протилежний
ogień,fire,огонь,вогонь
oglądać,to watch; to look at,смотреть,дивитися
ogród,garden,сад,сад
ojciec,father,отец,батько
okno,window,окно,вікно
oko,eye,глаз,око
okres,period; period of time,период; промежуток времени,період; проміжок часу
ołówek,pencil,карандаш,олівець
on,he,он,він
ona,she,она,вона
oni,they; they (masculine personal),они; они (мужчины/мужская группа),вони; вони (чоловічо-особова група)
opinia,opinion,мнение,думка
opis,description,описание,опис
opisać,to describe,описать,описати
opowieść,story; tale,рассказ; история,розповідь; історія
opublikować,to publish,опубликовать,опублікувати
ortografia,spelling; orthography,правописание,правопис
osiem,eight,восемь,вісім
osiemdziesiąt,eighty,восемьдесят,вісімдесят
osiemnaście,eighteen,восемнадцать,вісімнадцять
osoba,person,человек; лицо,особа; людина
osobisty,personal,личный,особистий
ostatni,last; final,последний; заключительный,останній; заключний
oświadczenie,statement; declaration,заявление; утверждение,заява; твердження
otworzyć,to open,открыть,відкрити
owca,sheep,овца,вівця
owoc,fruit,фрукт,фрукт
oznaczać,to mean; to indicate,означать; обозначать,означати; позначати
pamiętać,to remember,помнить,пам'ятати
papier,paper,бумага,папір
para,pair; couple,пара,пара
parasol,umbrella,зонт,парасоля
park,park,парк,парк
partner,partner,партнёр,партнер
pasować,to fit; to suit,подходить,підходити
paszport,passport,паспорт,паспорт
patrzeć,to look; to watch,смотреть,дивитися
październik,October,октябрь,жовтень
pełny,full,полный,повний
piątek,Friday,пятница,п’ятниця
piąty,fifth,пятый,п’ятий
pić,to drink,пить,пити
pięć,five,пять,п’ять
pięćdziesiąt,fifty,пятьдесят,п’ятдесят
piękny,beautiful,красивый,красивий
pielęgniarka,nurse,медсестра,медсестра
pieniądze,money,деньги,гроші
pieprz,pepper,перец,перець
pies,dog,собака,собака
piętnaście,fifteen,пятнадцать,п’ятнадцять
piłka,ball,мяч,м’яч
piosenka,song,песня,пісня
pisać,to write,писать,писати
pisanie,writing,написание,написання
pisarz,writer,писатель,письменник
płacić,to pay,платить,платити
plan,plan,план,план
płaszcz,coat,пальто,пальто
plaża,beach,пляж,пляж
plecy,back,спина,спина
pływanie,swimming,плавание,плавання
pociąg,train,поезд,поїзд
początek,beginning,начало,початок
pod,under,под,під
podczas,during,во время,під час
podekscytowany,excited,взволнованный,схвильований
podłoga,floor,пол,підлога
podobny,similar,похожий,схожий
podróż,trip / journey,поездка / путешествие,подорож
podróżować,to travel,путешествовать,подорожувати
podwórko,yard,двор,подвір’я
pogoda,weather,погода,погода
pokazać,to show,показать,показати
pokój,room,комната,кімната
policja,police,полиция,поліція
policjant,police officer,полицейский,поліцейський
północ,midnight / north,полночь / север,опівніч / північ
połowa,half,половина,половина
położyć,to put / to lay,положить,покласти
południe,noon / south,полдень / юг,полудень / південь
pomagać,to help,помогать,допомагати
pomarańcza,orange,апельсин,апельсин
pomidor,tomato,помидор,помідор
pomiędzy,between,между,між
pomysł,idea,идея,ідея
poniedziałek,Monday,понедельник,понеділок
popołudnie,afternoon,послеобеденное время,післяобідній час
poprawić,to improve / to correct,улучшить / исправить,покращити / виправити
poprawny,correct,правильный,правильний
popularny,popular,популярный,популярний
porównać,to compare,сравнить,порівняти
posiłek,meal,приём пищи,прийом їжі
potrzebować,to need,нуждаться / нуждаться в,потребувати
powiedzieć,to say / to tell,сказать,сказати
powietrze,air,воздух,повітря
powinien,should,должен,повинен
powód,reason,причина,причина
powszechny,common,распространённый,поширений
powtórzyć,to repeat,повторить,повторити
później,later,позже,пізніше
późno,late,поздно,пізно
pozwolić,to allow / to let,позволить / разрешить,дозволити
pozytywny,positive,положительный,позитивний
praca,work / job,работа,робота
pracować,to work,работать,працювати
pracownik,employee / worker,работник / сотрудник,працівник / співробітник
praktyka,practice,практика,практика
prawdopodobnie,probably,вероятно,ймовірно
prawdziwy,true / real,настоящий / истинный,справжній
prawo,law / right,закон / право,закон / право
preferować,to prefer,предпочитать,надавати перевагу
prezent,gift / present,подарок,подарунок
problem,problem,проблема,проблема
próbować,to try,пытаться / пробовать,намагатися / пробувати
produkt,product,продукт / товар,продукт / товар
program,program,программа,програма
projekt,project,проект,проєкт
proszę,please / you're welcome,пожалуйста / не за что,будь ласка / нема за що
prowadzić,to drive / to lead,водить / руководить,керувати / вести
prysznic,shower,душ,душ
przeciwny,opposite,противоположный,протилежний
przed,before / in front of,перед / до,перед / до
przedmiot,object / subject,предмет,предмет
przedstawić,to introduce / to present,представить,представити
przestraszony,scared / frightened,испуганный,наляканий
przestrzeń,space,пространство,простір
przesunąć,to move / to shift,сдвинуть / передвинуть,посунути / пересунути
przeszłość,past,прошлое,минуле
przez,through / across,через / сквозь,через / крізь
przód,front,передняя часть / перед,передня частина / перед
przybyć,to arrive,прибыть / приехать,прибути / приїхати
przygotować,to prepare,подготовить,підготувати
przyjaciel,friend,друг,друг
przyjazny,friendly,дружелюбный,дружній
przyjść,to come,прийти,прийти
przykład,example,пример,приклад
przynieść,to bring,принести,принести
przyszłość,future,будущее,майбутнє
ptak,bird,птица,птах
pudełko,box,коробка,коробка
punkt,point,точка / пункт,точка / пункт
pyszny,delicious,вкусный,смачний
pytać,to ask,спрашивать,запитувати
pytanie,question,вопрос,питання
rachunek,bill / check,счёт,рахунок
rada,advice,совет,порада
radio,radio,радио,радіо
rano,in the morning,утром,вранці
raport,report,отчёт,звіт
raz,once / one time,раз / один раз,раз / один раз
razem,together,вместе,разом
ręka,hand / arm,рука,рука
restauracja,restaurant,ресторан,ресторан
robić,to do / to make,делать,робити
rodzina,family,семья,сім’я
rok,year,год,рік
rolnik,farmer,фермер,фермер
roślina,plant,растение,рослина
rosnąć,to grow,расти,рости
rower,bicycle / bike,велосипед,велосипед
rozmawiać,to talk,разговаривать,розмовляти
rozmowa,conversation,разговор,розмова
różnica,difference,разница,різниця
różowy,pink,розовый,рожевий
rozumieć,to understand,понимать,розуміти
ruch,movement / traffic,движение / дорожное движение,рух / дорожній рух
rutyna,routine,распорядок / рутина,розпорядок / рутина
ryba,fish,рыба,риба
rynek,market,рынок,ринок
rysować,to draw,рисовать,малювати
ryż,rice,рис,рис
rzecz,thing,вещь,річ
rzeka,river,река,річка
sałatka,salad,салат,салат
samochód,car,автомобиль / машина,автомобіль / машина
samolot,airplane,самолёт,літак
sąsiad,neighbor,сосед,сусід
sąsiedztwo,neighborhood,соседство,сусідство
ściana,wall,стена,стіна
sekcja,section,раздел / секция,розділ / секція
sekunda,second,секунда,секунда
ser,cheese,сыр,сир
siebie,oneself,себя,себе
siedem,seven,семь,сім
siedemdziesiąt,seventy,семьдесят,сімдесят
siedemnaście,seventeen,семнадцать,сімнадцять
siedzieć,to sit,сидеть,сидіти
sierpień,August,август,серпень
silny,strong,сильный,сильний
siłownia,gym,спортзал,спортзал
siostra,sister,сестра,сестра
sklep,shop / store,магазин,магазин
skończyć,to finish,закончить,закінчити
skręcić,to turn,повернуть / свернуть,повернути / звернути
sławny,famous,знаменитый,відомий
śledzić,to follow / to track,следить / отслеживать,стежити / відстежувати
słoń,elephant,слон,слон
słońce,sun,солнце,сонце
słownik,dictionary,словарь,словник
słowo,word,слово,слово
słuchać,to listen,слушать,слухати
słyszeć,to hear,слышать,чути
śmietana,sour cream,сметана,сметана
smutny,sad,грустный,сумний
śniadanie,breakfast,завтрак,сніданок
śnieg,snow,снег,сніг
sobota,Saturday,суббота,субота
sok,juice,сок,сік
sól,salt,соль,сіль
spać,to sleep,спать,спати
specjalny,special,особенный / специальный,особливий / спеціальний
śpiewak,singer,певец,співак
spódnica,skirt,юбка,спідниця
spodnie,trousers / pants,брюки,штани
sport,sport,спорт,спорт
sposób,way / method,способ / метод,спосіб / метод
spotkać,to meet,встретить / встретиться,зустріти / зустрітися
spotkanie,meeting,встреча,зустріч
sprawdzić,to check,проверить,перевірити
sprzedać,to sell,продать,продати
środa,Wednesday,среда,середа
stać,to stand / to cost,стоять / стоить,стояти / коштувати
stacja,station,станция,станція
stary,old,старый,старий
sto,hundred,сто,сто
stół,table,стол,стіл
stolica,capital,столица,столиця
stopa,foot,ступня / стопа,стопа / ступня
straszny,scary / terrible,страшный / ужасный,страшний / жахливий
strona,side / page,сторона / страница,сторона / сторінка
student,student,студент,студент
styczeń,January,январь,січень
styl,style,стиль,стиль
sukces,success,успех,успіх
sukienka,dress,платье,сукня
supermarket,supermarket,супермаркет,супермаркет
sweter,sweater,свитер,светр
świat,world,мир,світ
światło,light,свет,світло
świetny,great / excellent,отличный,чудовий
świnia,pig,свинья,свиня
syn,son,сын,син
sypialnia,bedroom,спальня,спальня
sytuacja,situation,ситуация,ситуація
szary,gray / grey,серый,сірий
szczegół,detail,деталь,деталь
szczęśliwy,happy,счастливый,щасливий
sześć,six,шесть,шість
sześćdziesiąt,sixty,шестьдесят,шістдесят
szesnaście,sixteen,шестнадцать,шістнадцять
szkło,glass,стекло,скло
szkoła,school,школа,школа
szpital,hospital,больница,лікарня
sztuka,art,искусство,мистецтво
szybki,fast / quick,быстрый,швидкий
szybko,quickly / fast,быстро,швидко
tak,yes,да,так
taksówka,taxi,такси,таксі
talerz,plate,тарелка,тарілка
tam,there,там,там
tancerz,dancer,танцор,танцівник
tańczyć,to dance,танцевать,танцювати
tani,cheap,дешёвый,дешевий
taniec,dance,танец,танець
tata,dad / father,папа / отец,тато / батько
teatr,theater / theatre,театр,театр
tekst,text,текст,текст
telefon,telephone / phone,телефон,телефон
telewizor,TV / television,телевизор,телевізор
temat,topic / subject,тема,тема
ten,this / this one,этот,цей
tenis,tennis,теннис,теніс
teraz,now,сейчас,зараз
test,test,тест,тест
też,also / too,тоже / также,теж / також
to,this / it,это,це
toaleta,toilet / restroom,туалет,туалет
torba,bag,сумка,сумка
trudny,difficult / hard,трудный,складний
trzeci,third,третий,третій
trzy,three,три,три
trzydzieści,thirty,тридцать,тридцять
trzynaście,thirteen,тринадцать,тринадцять
turysta,tourist,турист,турист
tutaj,here,здесь,тут
twarz,face,лицо,обличчя
twój,your,твой,твій
tworzyć,to create,создавать,створювати
ty,you,ты,ти
tydzień,week,неделя,тиждень
tylko,only,только,тільки
typ,type / kind,тип / вид,тип / вид
tysiąc,thousand,тысяча,тисяча
tytuł,title,название / заголовок,назва / заголовок
ubrania,clothes,одежда,одяг
ucho,ear,ухо,вухо
uczucie,feeling,чувство,почуття
uczyć,to learn / to teach,учить / учиться,вчити / вчитися
ukończyć,to complete / to finish,закончить / завершить,закінчити
ulica,street,улица,вулиця
ulubiony,favorite,любимый,улюблений
umiejętność,skill / ability,навык / способность,навичка / здатність
umrzeć,to die,умереть,померти
uniwersytet,university,университет,університет
upaść,to fall,упасть,впасти
urodziny,birthday,день рождения,день народження
użyteczny,useful,полезный,корисний
używać,to use,использовать,використовувати
wakacje,holiday / vacation,каникулы / отпуск,канікули / відпустка
warzywo,vegetable,овощ,овоч
wąż,snake,змея,змія
ważny,important,важный,важливий
wciąż,still,всё ещё,досі / все ще
wcześnie,early,рано,рано
wczoraj,yesterday,вчера,вчора
weekend,weekend,выходные,вихідні
wiadomość,message / news,сообщение / новость,повідомлення / новина
wiadomości,news,новости,новини
wideo,video,видео,відео
widzieć,to see,видеть,бачити
więc,so / therefore,так что / поэтому,тому / отже
więcej,more,больше,більше
wieczór,evening,вечер,вечір
wiedzieć,to know,знать,знати
wiek,age,возраст,вік
wiele,many / much,много,багато
wierzyć,to believe,верить,вірити
wino,wine,вино,вино
wiosna,spring,весна,весна
wkrótce,soon,скоро,скоро
własny,own,собственный,власний
włosy,hair,волосы,волосся
woda,water,вода,вода
wokoło,around,вокруг,навколо
wolny,free / slow,свободный / медленный,вільний / повільний
wrócić,to return / to come back,вернуться,повернутися
wrzesień,September,сентябрь,вересень
wschód,east / sunrise,восток / восход,схід / схід сонця
wszyscy,everyone / everybody,все / все люди,усі / всі
wszystko,everything,всё,все
wtedy,then,тогда,тоді
wtorek,Tuesday,вторник,вівторок
wujek,uncle,дядя,дядько
wybierać,to choose,выбирать,вибирати
wydarzenie,event,событие,подія
wydawać,to spend / to seem,тратить / казаться,витрачати / здаватися
wygrać,to win,выиграть,виграти
wyjaśnić,to explain,объяснить,пояснити
wykres,chart / graph,график / диаграмма,графік / діаграма
wynik,result,результат,результат
wysłać,to send,отправить,надіслати
wysoki,tall / high,высокий,високий
wyspa,island,остров,острів
ząb,tooth,зуб,зуб
żaba,frog,лягушка,жаба
zabawa,fun / party,веселье / вечеринка,розвага / вечірка
zabawny,funny,смешной,смішний
zachód,west / sunset,запад / закат,захід / захід сонця
zacząć,to start / to begin,начать,почати
zaczynać,to start / to begin,начинать,починати
zadzwonić,to call,позвонить,зателефонувати
zainteresowanie,interest,интерес,інтерес
zainteresowany,interested,заинтересованный,зацікавлений
zajęty,busy,занятый,зайнятий
zakupy,shopping,покупки,покупки
zamknąć,to close,закрыть,закрити
zamówienie,order,заказ,замовлення
zapomnieć,to forget,забыть,забути
zasada,rule,правило,правило
zatrzymać,to stop / to hold,остановить / удержать,зупинити / утримати
zawierać,to contain,содержать,містити
zawsze,always,всегда,завжди
zdanie,sentence,предложение,речення
zdjęcie,photo / picture,фотография / фото,фотографія / фото
zdrowie,health,здоровье,здоров’я
zdrowy,healthy,здоровый,здоровий
zegar,clock,часы,годинник
zespół,team / group,команда / группа,команда / група
zgadywać,to guess,угадывать,вгадувати
zgubić,to lose,потерять,загубити
zielony,green,зелёный,зелений
ziemia,earth / soil,земля,земля
ziemniak,potato,картофель / картошка,картопля
zima,winter,зима,зима
zimny,cold,холодный,холодний
złamać,to break,сломать,зламати
zły,bad / angry,плохой / злой,поганий / злий
zmęczony,tired,уставший,втомлений
zmiana,change,изменение / перемена,зміна
znaczenie,meaning,значение,значення
znaleźć,to find,найти,знайти
znowu,again,снова,знову
znudzony,bored,скучающий,знудьгований
żółty,yellow,жёлтый,жовтий
żona,wife,жена,дружина
żonaty,married,женатый,одружений
zostać,to stay / to become,остаться / стать,залишитися / стати
zupa,soup,суп,суп
zwierzę,animal,животное,тварина
zwykle,usually,обычно,зазвичай
żyć,to live,жить,жити
życie,life,жизнь,життя
1 polish english russian ukrainian
2 adres address адрес адреса
3 akapit paragraph абзац абзац
4 akcja action действие дія
5 aktor actor актёр актор
6 aktorka actress актриса акторка
7 aktywność activity активность активність
8 ale but но але
9 artykuł article статья стаття
10 artysta artist артист артист
11 autobus bus автобус автобус
12 babcia grandmother бабушка бабуся
13 banan banana банан банан
14 bardzo very очень дуже
15 basen swimming pool бассейн басейн
16 będę I will be я буду я буду
17 bez without без без
18 biały white белый білий
19 biblioteka library библиотека бібліотека
20 biedny poor бедный бідний
21 biegać to run бегать бігати
22 bilet ticket билет квиток
23 biurko desk письменный стол письмовий стіл
24 biuro office офис офіс
25 biznes business бизнес бізнес
26 błąd mistake / error ошибка помилка
27 blisko close / near близко близько
28 blond blond / blonde светловолосый білявий
29 bogaty rich богатый багатий
30 brać to take брать брати
31 brat brother брат брат
32 brązowy brown коричневый коричневий
33 brudny dirty грязный брудний
34 budować to build строить будувати
35 budynek building здание будівля
36 but shoe туфля / ботинок черевик / туфля
37 butelka bottle бутылка пляшка
38 cebula onion лук цибуля
39 cena price цена ціна
40 centrum centre / center центр центр
41 chciałbym I would like я хотел бы я хотів би
42 chcieć to want хотеть хотіти
43 chleb bread хлеб хліб
44 chłodny cool прохладный прохолодний
45 chłopak boy / boyfriend парень хлопець
46 chłopiec boy мальчик хлопчик
47 chodzić to walk / to go regularly ходить ходити
48 chory sick / ill больной хворий
49 ciąć to cut резать різати
50 ciało body тело тіло
51 ciasto cake торт торт
52 cichy quiet тихий тихий
53 ciemny dark тёмный темний
54 ciepły warm тёплый теплий
55 ciężarówka truck грузовик вантажівка
56 ciocia aunt тётя тітка
57 cokolwiek anything что угодно що завгодно
58 córka daughter дочь донька
59 coś something что-то щось
60 cudowny wonderful чудесный чудовий
61 cukier sugar сахар цукор
62 ćwiartka quarter четверть чверть
63 ćwiczenie exercise упражнение вправа
64 czarny black чёрный чорний
65 czas time время час
66 czasami sometimes иногда іноді
67 czekać wait ждать чекати
68 czekolada chocolate шоколад шоколад
69 czerwiec June июнь червень
70 czerwony red красный червоний
71 cześć hello; hi привет привіт
72 część part; piece часть частина
73 często often часто часто
74 czterdzieści forty сорок сорок
75 czternaście fourteen четырнадцать чотирнадцять
76 cztery four четыре чотири
77 czujny alert; vigilant бдительный пильний
78 Czwartek Thursday четверг четвер
79 czwarty fourth четвёртый четвертий
80 czysty clean чистый чистий
81 czytać read читать читати
82 czytanie reading чтение читання
83 czytelnik reader читатель читач
84 dać give дать дати
85 daleko far далеко далеко
86 darmowy free бесплатный безкоштовний
87 decydować decide решать вирішувати
88 deszcz rain дождь дощ
89 dialog dialogue диалог діалог
90 dieta diet диета дієта
91 dla for для для
92 dlaczego why почему чому
93 długi long длинный довгий
94 długopis pen ручка ручка
95 do to; until до до
96 dobry good хороший добрий
97 dobrze well; okay хорошо добре
98 dodać add добавить додати
99 dodatkowy additional дополнительный додатковий
100 dołączyć join присоединиться приєднатися
101 dom house; home дом дім
102 dorosły adult взрослый дорослий
103 dość enough достаточно достатньо
104 doskonały excellent; perfect отличный; идеальный відмінний; ідеальний
105 dostać get; receive получить отримати
106 dowolny any любой будь-який
107 droga road; way дорога; путь дорога; шлях
108 drogi expensive; dear дорогой дорогий
109 drużyna team команда команда
110 drzewo tree дерево дерево
111 drzwi door дверь двері
112 dużo a lot; much; many много багато
113 duży big; large большой великий
114 dwa two два два
115 dwadzieścia twenty двадцать двадцять
116 dwanaście twelve двенадцать дванадцять
117 dyskutować discuss обсуждать обговорювати
118 dziadek grandfather дедушка дідусь
119 dziecko child ребёнок дитина
120 dziękować thank благодарить дякувати
121 dziękuję thank you спасибо дякую
122 dzielić divide; share делить; делиться ділити; ділитися
123 dzień day день день
124 dziesięć ten десять десять
125 dziewczyna girl девушка; девочка дівчина; дівчинка
126 dziewięć nine девять дев'ять
127 dziewięćdziesiąt ninety девяносто дев'яносто
128 dziewiętnaście nineteen девятнадцать дев'ятнадцять
129 dżinsy jeans джинсы джинси
130 dziś today сегодня сьогодні
131 dźwięk sound звук звук
132 egzamin exam экзамен іспит
133 ekscytujący exciting захватывающий захопливий
134 fakt fact факт факт
135 fałszywy false; fake ложный; поддельный хибний; підроблений
136 fantastyczny fantastic фантастический; замечательный фантастичний; чудовий
137 farba paint краска фарба
138 farma farm ферма ферма
139 festiwal festival фестиваль фестиваль
140 film film; movie фильм фільм
141 finałowy final финальный фінальний
142 fioletowy purple фиолетовый фіолетовий
143 firma company; firm компания компанія
144 forma form; shape форма форма
145 fortepian piano фортепиано фортепіано
146 fotografia photograph; photography фотография; фотография как искусство фотографія; фотографія як мистецтво
147 fraza phrase фраза фраза
148 funt pound фунт фунт
149 gazeta newspaper газета газета
150 gdzie where где де
151 geografia geography география географія
152 gitara guitar гитара гітара
153 głodny hungry голодный голодний
154 głowa head голова голова
155 główny main; main/primary главный головний
156 godzina hour; o'clock час година
157 góra mountain; hill гора; холм гора; пагорб
158 gorący hot горячий гарячий
159 gotować to cook готовить готувати
160 gotowanie cooking готовка; приготовление пищи готування
161 gotowy ready готовый готовий
162 gra game; play игра гра
163 grać to play играть грати
164 gracz player игрок гравець
165 gruby fat; thick толстый товстий
166 grudzień December декабрь грудень
167 grupa group группа група
168 gwiazda star звезда зірка
169 hej hey; hi эй; привет гей; привіт
170 herbata tea чай чай
171 historia history; story история; рассказ історія; розповідь
172 hobby hobby хобби хобі
173 hotel hotel отель; гостиница готель
174 ich their их їхній
175 imię name; first name имя ім'я
176 impreza party; event вечеринка; мероприятие вечірка; захід
177 informacja information информация інформація
178 inny other; different другой; иной інший
179 inteligentny intelligent; smart умный; интеллектуальный розумний; інтелектуальний
180 interesujący interesting интересный цікавий
181 internet internet интернет інтернет
182 iść to go; to walk идти йти
183 her её її
184 jabłko apple яблоко яблуко
185 jajko egg яйцо яйце
186 jak how; like как як
187 jeden one; one (masculine) один один
188 jedenaście eleven одиннадцать одинадцять
189 jednak however; nevertheless однако; всё же однак; все ж
190 jedzenie food; eating еда; питание їжа; харчування
191 jego his его його
192 jeść to eat есть їсти
193 jeśli if если якщо
194 jeździć to travel; to ride; to go regularly ездить; путешествовать їздити; подорожувати
195 język language; tongue язык мова; язик
196 jutro tomorrow завтра завтра
197 kanapka sandwich бутерброд; сэндвич бутерброд; сендвіч
198 kapelusz hat шляпа капелюх
199 kąpiel bath; bathing ванна; купание ванна; купання
200 kariera career карьера кар'єра
201 karta card; menu карта; карточка; меню картка; меню
202 kawa coffee кофе кава
203 kawał piece; bit кусок; немного шматок; трохи
204 kawiarnia café; coffee shop кафе кав'ярня; кафе
205 każdy every; each; everyone каждый кожен
206 kelner waiter официант офіціант
207 kiedy when когда коли
208 kiedykolwiek ever; at any time когда-либо коли-небудь
209 kierowca driver водитель водій
210 kilka several; a few несколько кілька
211 klasa class класс клас
212 klub club клуб клуб
213 klucz key ключ ключ
214 kobieta woman женщина жінка
215 kochać to love любить кохати; любити
216 kolacja dinner; supper ужин вечеря
217 kolor color цвет колір
218 komputer computer компьютер комп'ютер
219 koń horse лошадь кінь
220 koncert concert концерт концерт
221 koniec end конец кінець
222 koszt cost стоимость; цена вартість; ціна
223 koszula shirt рубашка сорочка
224 koszykówka basketball баскетбол баскетбол
225 kot cat кот; кошка кіт; кішка
226 kraj country; land страна; государство країна; держава
227 krótki short короткий короткий
228 krowa cow корова корова
229 krzesło chair стул стілець
230 książka book книга книга
231 kto who кто хто
232 który which; which one который; какой який; котрий
233 ktoś someone; somebody кто-то; кто-нибудь хтось; хтось
234 kubek cup; mug чашка чашка
235 kuchnia kitchen; cuisine кухня; кухня (национальная) кухня
236 kultura culture культура культура
237 kupić to buy купить купити
238 kurczak chicken курица; цыплёнок курка; курча
239 kurs course; course of study курс курс
240 kurtka jacket куртка куртка
241 kuzyn cousin (male) двоюродный брат; кузен двоюрідний брат; кузен
242 kwiat flower цветок квітка
243 kwiecień April апрель квітень
244 ładny pretty; nice красивый; симпатичный гарний; симпатичний
245 lato summer лето літо
246 łatwy easy лёгкий легкий
247 łazienka bathroom ванная комната ванна кімната
248 lekarz doctor врач лікар
249 lekcja lesson урок урок
250 lepszy better лучший; лучше кращий; краще
251 lew lion лев лев
252 leżeć to lie; to be lying лежать лежати
253 licznik meter; counter счётчик; измерительный прибор лічильник; вимірювальний прилад
254 linia line линия; линия лінія
255 lipiec July июль липень
256 list letter письмо лист
257 lista list список список
258 listopad November ноябрь листопад
259 literować to spell произносить по буквам називати по літерах
260 lód ice лёд лід
261 lody ice cream мороженое морозиво
262 łódź boat лодка човен
263 lokalny local местный місцевий
264 lot flight полёт; рейс політ; рейс
265 lotnisko airport аэропорт аеропорт
266 łóżko bed кровать ліжко
267 lub or или або
268 lubić to like любить; нравиться любити; подобатися
269 ludzie people люди люди
270 lunch lunch обед; ланч обід; ланч
271 luty February февраль лютий
272 magazyn magazine; warehouse журнал; склад журнал; склад
273 maj May май травень
274 mały small; little маленький малий
275 mama mum; mother мама мама
276 mapa map карта карта
277 marchew carrot морковь морква
278 marzec March март березень
279 masło butter сливочное масло вершкове масло
280 maszyna machine машина; механизм машина; механізм
281 matka mother мать мати
282 mąż husband муж чоловік
283 menu menu меню меню
284 mężczyzna man мужчина чоловік
285 miasto city; town город місто
286 mieć to have иметь мати
287 miejsce place; seat место місце
288 miesiąc month месяц місяць
289 mięso meat мясо м'ясо
290 mieszkanie flat; apartment квартира квартира
291 mila mile миля миля
292 milion million миллион мільйон
293 miły nice; kind милый; добрый милий; добрий
294 minuta minute минута хвилина
295 mleko milk молоко молоко
296 młody young молодой молодий
297 mnie me меня; мне мене; мені
298 móc to be able to; can мочь могти
299 model model модель модель
300 mógł could; was able to мог міг
301 mój my мой мій
302 moment moment момент момент
303 mówić to speak; to say говорить говорити
304 może maybe; perhaps может быть; возможно можливо; мабуть
305 możliwy possible возможный можливий
306 musieć to have to; must быть вынужденным; должен мусити; бути змушеним
307 muzeum museum музей музей
308 muzyka music музыка музика
309 my we мы ми
310 myć to wash мыть мити
311 myśleć to think думать думати
312 nad over; above; by над; выше; у над; біля
313 najlepszy best лучший найкращий
314 najpierw first; firstly сначала; сперва спочатку
315 najwięcej the most больше всего; наиболее найбільше
316 napełniać to fill наполнять наповнювати
317 naprawdę really действительно; правда справді; насправді
318 nas us нас нас
319 następny next следующий наступний
320 nasz our наш наш
321 naturalny natural естественный; натуральный природний; натуральний
322 nauczyciel teacher учитель учитель
323 nauka learning; science учёба; наука навчання; наука
324 naukowiec scientist учёный науковець
325 nawet even даже навіть
326 negatywny negative негативный негативний
327 nic nothing ничего; ничто нічого; ніщо
328 nie no; not не; нет не; ні
329 niebezpieczny dangerous опасный небезпечний
330 niebieski blue синий; голубой синій; блакитний
331 niedziela Sunday воскресенье неділя
332 niektóre some; certain некоторые деякі
333 niemowlę baby; infant младенец немовля
334 nienawidzić to hate ненавидеть ненавидіти
335 niesamowity amazing; incredible невероятный; потрясающий неймовірний; дивовижний
336 nigdy never никогда ніколи
337 nikt nobody; no one никто ніхто
338 niż than чем; нежели ніж
339 noc night ночь ніч
340 noga leg; foot нога нога
341 nos nose нос ніс
342 nosić to wear; to carry носить носити
343 notatka note заметка; записка нотатка; записка
344 nowoczesny modern современный сучасний
345 nowy new новый новий
346 nudny boring скучный нудний
347 numer number номер; число номер; число
348 obiekt object; facility объект об'єкт
349 obok next to; beside рядом; возле поруч; біля
350 obraz picture; image картина; изображение картина; зображення
351 obszar area область; район область; район
352 ocean ocean океан океан
353 odpowiedzieć to answer; to reply ответить відповісти
354 odwiedzać to visit посещать; навещать відвідувати
355 odwiedzający visitor посетитель відвідувач
356 odwrotny opposite; reverse обратный; противоположный зворотний; протилежний
357 ogień fire огонь вогонь
358 oglądać to watch; to look at смотреть дивитися
359 ogród garden сад сад
360 ojciec father отец батько
361 okno window окно вікно
362 oko eye глаз око
363 okres period; period of time период; промежуток времени період; проміжок часу
364 ołówek pencil карандаш олівець
365 on he он він
366 ona she она вона
367 oni they; they (masculine personal) они; они (мужчины/мужская группа) вони; вони (чоловічо-особова група)
368 opinia opinion мнение думка
369 opis description описание опис
370 opisać to describe описать описати
371 opowieść story; tale рассказ; история розповідь; історія
372 opublikować to publish опубликовать опублікувати
373 ortografia spelling; orthography правописание правопис
374 osiem eight восемь вісім
375 osiemdziesiąt eighty восемьдесят вісімдесят
376 osiemnaście eighteen восемнадцать вісімнадцять
377 osoba person человек; лицо особа; людина
378 osobisty personal личный особистий
379 ostatni last; final последний; заключительный останній; заключний
380 oświadczenie statement; declaration заявление; утверждение заява; твердження
381 otworzyć to open открыть відкрити
382 owca sheep овца вівця
383 owoc fruit фрукт фрукт
384 oznaczać to mean; to indicate означать; обозначать означати; позначати
385 pamiętać to remember помнить пам'ятати
386 papier paper бумага папір
387 para pair; couple пара пара
388 parasol umbrella зонт парасоля
389 park park парк парк
390 partner partner партнёр партнер
391 pasować to fit; to suit подходить підходити
392 paszport passport паспорт паспорт
393 patrzeć to look; to watch смотреть дивитися
394 październik October октябрь жовтень
395 pełny full полный повний
396 piątek Friday пятница п’ятниця
397 piąty fifth пятый п’ятий
398 pić to drink пить пити
399 pięć five пять п’ять
400 pięćdziesiąt fifty пятьдесят п’ятдесят
401 piękny beautiful красивый красивий
402 pielęgniarka nurse медсестра медсестра
403 pieniądze money деньги гроші
404 pieprz pepper перец перець
405 pies dog собака собака
406 piętnaście fifteen пятнадцать п’ятнадцять
407 piłka ball мяч м’яч
408 piosenka song песня пісня
409 pisać to write писать писати
410 pisanie writing написание написання
411 pisarz writer писатель письменник
412 płacić to pay платить платити
413 plan plan план план
414 płaszcz coat пальто пальто
415 plaża beach пляж пляж
416 plecy back спина спина
417 pływanie swimming плавание плавання
418 pociąg train поезд поїзд
419 początek beginning начало початок
420 pod under под під
421 podczas during во время під час
422 podekscytowany excited взволнованный схвильований
423 podłoga floor пол підлога
424 podobny similar похожий схожий
425 podróż trip / journey поездка / путешествие подорож
426 podróżować to travel путешествовать подорожувати
427 podwórko yard двор подвір’я
428 pogoda weather погода погода
429 pokazać to show показать показати
430 pokój room комната кімната
431 policja police полиция поліція
432 policjant police officer полицейский поліцейський
433 północ midnight / north полночь / север опівніч / північ
434 połowa half половина половина
435 położyć to put / to lay положить покласти
436 południe noon / south полдень / юг полудень / південь
437 pomagać to help помогать допомагати
438 pomarańcza orange апельсин апельсин
439 pomidor tomato помидор помідор
440 pomiędzy between между між
441 pomysł idea идея ідея
442 poniedziałek Monday понедельник понеділок
443 popołudnie afternoon послеобеденное время післяобідній час
444 poprawić to improve / to correct улучшить / исправить покращити / виправити
445 poprawny correct правильный правильний
446 popularny popular популярный популярний
447 porównać to compare сравнить порівняти
448 posiłek meal приём пищи прийом їжі
449 potrzebować to need нуждаться / нуждаться в потребувати
450 powiedzieć to say / to tell сказать сказати
451 powietrze air воздух повітря
452 powinien should должен повинен
453 powód reason причина причина
454 powszechny common распространённый поширений
455 powtórzyć to repeat повторить повторити
456 później later позже пізніше
457 późno late поздно пізно
458 pozwolić to allow / to let позволить / разрешить дозволити
459 pozytywny positive положительный позитивний
460 praca work / job работа робота
461 pracować to work работать працювати
462 pracownik employee / worker работник / сотрудник працівник / співробітник
463 praktyka practice практика практика
464 prawdopodobnie probably вероятно ймовірно
465 prawdziwy true / real настоящий / истинный справжній
466 prawo law / right закон / право закон / право
467 preferować to prefer предпочитать надавати перевагу
468 prezent gift / present подарок подарунок
469 problem problem проблема проблема
470 próbować to try пытаться / пробовать намагатися / пробувати
471 produkt product продукт / товар продукт / товар
472 program program программа програма
473 projekt project проект проєкт
474 proszę please / you're welcome пожалуйста / не за что будь ласка / нема за що
475 prowadzić to drive / to lead водить / руководить керувати / вести
476 prysznic shower душ душ
477 przeciwny opposite противоположный протилежний
478 przed before / in front of перед / до перед / до
479 przedmiot object / subject предмет предмет
480 przedstawić to introduce / to present представить представити
481 przestraszony scared / frightened испуганный наляканий
482 przestrzeń space пространство простір
483 przesunąć to move / to shift сдвинуть / передвинуть посунути / пересунути
484 przeszłość past прошлое минуле
485 przez through / across через / сквозь через / крізь
486 przód front передняя часть / перед передня частина / перед
487 przybyć to arrive прибыть / приехать прибути / приїхати
488 przygotować to prepare подготовить підготувати
489 przyjaciel friend друг друг
490 przyjazny friendly дружелюбный дружній
491 przyjść to come прийти прийти
492 przykład example пример приклад
493 przynieść to bring принести принести
494 przyszłość future будущее майбутнє
495 ptak bird птица птах
496 pudełko box коробка коробка
497 punkt point точка / пункт точка / пункт
498 pyszny delicious вкусный смачний
499 pytać to ask спрашивать запитувати
500 pytanie question вопрос питання
501 rachunek bill / check счёт рахунок
502 rada advice совет порада
503 radio radio радио радіо
504 rano in the morning утром вранці
505 raport report отчёт звіт
506 raz once / one time раз / один раз раз / один раз
507 razem together вместе разом
508 ręka hand / arm рука рука
509 restauracja restaurant ресторан ресторан
510 robić to do / to make делать робити
511 rodzina family семья сім’я
512 rok year год рік
513 rolnik farmer фермер фермер
514 roślina plant растение рослина
515 rosnąć to grow расти рости
516 rower bicycle / bike велосипед велосипед
517 rozmawiać to talk разговаривать розмовляти
518 rozmowa conversation разговор розмова
519 różnica difference разница різниця
520 różowy pink розовый рожевий
521 rozumieć to understand понимать розуміти
522 ruch movement / traffic движение / дорожное движение рух / дорожній рух
523 rutyna routine распорядок / рутина розпорядок / рутина
524 ryba fish рыба риба
525 rynek market рынок ринок
526 rysować to draw рисовать малювати
527 ryż rice рис рис
528 rzecz thing вещь річ
529 rzeka river река річка
530 sałatka salad салат салат
531 samochód car автомобиль / машина автомобіль / машина
532 samolot airplane самолёт літак
533 sąsiad neighbor сосед сусід
534 sąsiedztwo neighborhood соседство сусідство
535 ściana wall стена стіна
536 sekcja section раздел / секция розділ / секція
537 sekunda second секунда секунда
538 ser cheese сыр сир
539 siebie oneself себя себе
540 siedem seven семь сім
541 siedemdziesiąt seventy семьдесят сімдесят
542 siedemnaście seventeen семнадцать сімнадцять
543 siedzieć to sit сидеть сидіти
544 sierpień August август серпень
545 silny strong сильный сильний
546 siłownia gym спортзал спортзал
547 siostra sister сестра сестра
548 sklep shop / store магазин магазин
549 skończyć to finish закончить закінчити
550 skręcić to turn повернуть / свернуть повернути / звернути
551 sławny famous знаменитый відомий
552 śledzić to follow / to track следить / отслеживать стежити / відстежувати
553 słoń elephant слон слон
554 słońce sun солнце сонце
555 słownik dictionary словарь словник
556 słowo word слово слово
557 słuchać to listen слушать слухати
558 słyszeć to hear слышать чути
559 śmietana sour cream сметана сметана
560 smutny sad грустный сумний
561 śniadanie breakfast завтрак сніданок
562 śnieg snow снег сніг
563 sobota Saturday суббота субота
564 sok juice сок сік
565 sól salt соль сіль
566 spać to sleep спать спати
567 specjalny special особенный / специальный особливий / спеціальний
568 śpiewak singer певец співак
569 spódnica skirt юбка спідниця
570 spodnie trousers / pants брюки штани
571 sport sport спорт спорт
572 sposób way / method способ / метод спосіб / метод
573 spotkać to meet встретить / встретиться зустріти / зустрітися
574 spotkanie meeting встреча зустріч
575 sprawdzić to check проверить перевірити
576 sprzedać to sell продать продати
577 środa Wednesday среда середа
578 stać to stand / to cost стоять / стоить стояти / коштувати
579 stacja station станция станція
580 stary old старый старий
581 sto hundred сто сто
582 stół table стол стіл
583 stolica capital столица столиця
584 stopa foot ступня / стопа стопа / ступня
585 straszny scary / terrible страшный / ужасный страшний / жахливий
586 strona side / page сторона / страница сторона / сторінка
587 student student студент студент
588 styczeń January январь січень
589 styl style стиль стиль
590 sukces success успех успіх
591 sukienka dress платье сукня
592 supermarket supermarket супермаркет супермаркет
593 sweter sweater свитер светр
594 świat world мир світ
595 światło light свет світло
596 świetny great / excellent отличный чудовий
597 świnia pig свинья свиня
598 syn son сын син
599 sypialnia bedroom спальня спальня
600 sytuacja situation ситуация ситуація
601 szary gray / grey серый сірий
602 szczegół detail деталь деталь
603 szczęśliwy happy счастливый щасливий
604 sześć six шесть шість
605 sześćdziesiąt sixty шестьдесят шістдесят
606 szesnaście sixteen шестнадцать шістнадцять
607 szkło glass стекло скло
608 szkoła school школа школа
609 szpital hospital больница лікарня
610 sztuka art искусство мистецтво
611 szybki fast / quick быстрый швидкий
612 szybko quickly / fast быстро швидко
613 tak yes да так
614 taksówka taxi такси таксі
615 talerz plate тарелка тарілка
616 tam there там там
617 tancerz dancer танцор танцівник
618 tańczyć to dance танцевать танцювати
619 tani cheap дешёвый дешевий
620 taniec dance танец танець
621 tata dad / father папа / отец тато / батько
622 teatr theater / theatre театр театр
623 tekst text текст текст
624 telefon telephone / phone телефон телефон
625 telewizor TV / television телевизор телевізор
626 temat topic / subject тема тема
627 ten this / this one этот цей
628 tenis tennis теннис теніс
629 teraz now сейчас зараз
630 test test тест тест
631 też also / too тоже / также теж / також
632 to this / it это це
633 toaleta toilet / restroom туалет туалет
634 torba bag сумка сумка
635 trudny difficult / hard трудный складний
636 trzeci third третий третій
637 trzy three три три
638 trzydzieści thirty тридцать тридцять
639 trzynaście thirteen тринадцать тринадцять
640 turysta tourist турист турист
641 tutaj here здесь тут
642 twarz face лицо обличчя
643 twój your твой твій
644 tworzyć to create создавать створювати
645 ty you ты ти
646 tydzień week неделя тиждень
647 tylko only только тільки
648 typ type / kind тип / вид тип / вид
649 tysiąc thousand тысяча тисяча
650 tytuł title название / заголовок назва / заголовок
651 ubrania clothes одежда одяг
652 ucho ear ухо вухо
653 uczucie feeling чувство почуття
654 uczyć to learn / to teach учить / учиться вчити / вчитися
655 ukończyć to complete / to finish закончить / завершить закінчити
656 ulica street улица вулиця
657 ulubiony favorite любимый улюблений
658 umiejętność skill / ability навык / способность навичка / здатність
659 umrzeć to die умереть померти
660 uniwersytet university университет університет
661 upaść to fall упасть впасти
662 urodziny birthday день рождения день народження
663 użyteczny useful полезный корисний
664 używać to use использовать використовувати
665 wakacje holiday / vacation каникулы / отпуск канікули / відпустка
666 warzywo vegetable овощ овоч
667 wąż snake змея змія
668 ważny important важный важливий
669 wciąż still всё ещё досі / все ще
670 wcześnie early рано рано
671 wczoraj yesterday вчера вчора
672 weekend weekend выходные вихідні
673 wiadomość message / news сообщение / новость повідомлення / новина
674 wiadomości news новости новини
675 wideo video видео відео
676 widzieć to see видеть бачити
677 więc so / therefore так что / поэтому тому / отже
678 więcej more больше більше
679 wieczór evening вечер вечір
680 wiedzieć to know знать знати
681 wiek age возраст вік
682 wiele many / much много багато
683 wierzyć to believe верить вірити
684 wino wine вино вино
685 wiosna spring весна весна
686 wkrótce soon скоро скоро
687 własny own собственный власний
688 włosy hair волосы волосся
689 woda water вода вода
690 wokoło around вокруг навколо
691 wolny free / slow свободный / медленный вільний / повільний
692 wrócić to return / to come back вернуться повернутися
693 wrzesień September сентябрь вересень
694 wschód east / sunrise восток / восход схід / схід сонця
695 wszyscy everyone / everybody все / все люди усі / всі
696 wszystko everything всё все
697 wtedy then тогда тоді
698 wtorek Tuesday вторник вівторок
699 wujek uncle дядя дядько
700 wybierać to choose выбирать вибирати
701 wydarzenie event событие подія
702 wydawać to spend / to seem тратить / казаться витрачати / здаватися
703 wygrać to win выиграть виграти
704 wyjaśnić to explain объяснить пояснити
705 wykres chart / graph график / диаграмма графік / діаграма
706 wynik result результат результат
707 wysłać to send отправить надіслати
708 wysoki tall / high высокий високий
709 wyspa island остров острів
710 ząb tooth зуб зуб
711 żaba frog лягушка жаба
712 zabawa fun / party веселье / вечеринка розвага / вечірка
713 zabawny funny смешной смішний
714 zachód west / sunset запад / закат захід / захід сонця
715 zacząć to start / to begin начать почати
716 zaczynać to start / to begin начинать починати
717 zadzwonić to call позвонить зателефонувати
718 zainteresowanie interest интерес інтерес
719 zainteresowany interested заинтересованный зацікавлений
720 zajęty busy занятый зайнятий
721 zakupy shopping покупки покупки
722 zamknąć to close закрыть закрити
723 zamówienie order заказ замовлення
724 zapomnieć to forget забыть забути
725 zasada rule правило правило
726 zatrzymać to stop / to hold остановить / удержать зупинити / утримати
727 zawierać to contain содержать містити
728 zawsze always всегда завжди
729 zdanie sentence предложение речення
730 zdjęcie photo / picture фотография / фото фотографія / фото
731 zdrowie health здоровье здоров’я
732 zdrowy healthy здоровый здоровий
733 zegar clock часы годинник
734 zespół team / group команда / группа команда / група
735 zgadywać to guess угадывать вгадувати
736 zgubić to lose потерять загубити
737 zielony green зелёный зелений
738 ziemia earth / soil земля земля
739 ziemniak potato картофель / картошка картопля
740 zima winter зима зима
741 zimny cold холодный холодний
742 złamać to break сломать зламати
743 zły bad / angry плохой / злой поганий / злий
744 zmęczony tired уставший втомлений
745 zmiana change изменение / перемена зміна
746 znaczenie meaning значение значення
747 znaleźć to find найти знайти
748 znowu again снова знову
749 znudzony bored скучающий знудьгований
750 żółty yellow жёлтый жовтий
751 żona wife жена дружина
752 żonaty married женатый одружений
753 zostać to stay / to become остаться / стать залишитися / стати
754 zupa soup суп суп
755 zwierzę animal животное тварина
756 zwykle usually обычно зазвичай
757 żyć to live жить жити
758 życie life жизнь життя
+843
View File
@@ -0,0 +1,843 @@
polish,english,russian,ukrainian
adwokat,lawyer,адвокат,адвокат
akceptuję,I accept,я принимаю,я приймаю
aktywny,active,активный,активний
albo,or,или,або
alternatywa,alternative,альтернатива,альтернатива
analizować,to analyze,анализировать,аналізувати
aplikacja,application,приложение,додаток
aplikować,to apply,подавать заявление,подавати заяву
architekt,architect,архитектор,архітектор
architektura,architecture,архитектура,архітектура
armia,army,армия,армія
asystent,assistant,ассистент,асистент
atrakcyjny,attractive,привлекательный,привабливий
autor,author,автор,автор
,as much as / until,аж / пока,аж / поки
badacz,researcher,исследователь,дослідник
badania,research,исследования,дослідження
badanie,examination / study,обследование / исследование,обстеження / дослідження
bezpieczny,safe,безопасный,безпечний
bezpośredni,direct,прямой,прямий
biały,white,белый,білий
bić,to beat,бить,бити
bieg,run / race,бег / забег,біг / забіг
biegacz,runner,бегун,бігун
bieganie,running,бег,біг
biologia,biology,биология,біологія
biznesmen,businessman,бизнесмен,бізнесмен
biżuteria,jewelry,ювелирные изделия,ювелірні вироби
błąd,mistake,ошибка,помилка
blisko,near,близко,близько
bliźniak,twin,близнец,близнюк
blok,block,блок,блок
błyskotliwy,brilliant,блестящий,блискучий
bóg,god,бог,бог
bohater,hero,герой,герой
bok,side,бок,бік
ból,pain,боль,біль
brakujący,missing,отсутствующий,відсутній
brama,gate,ворота,ворота
brązowy,brown,коричневый,коричневий
brzmieć,to sound,звучать,звучати
burmistrz,mayor,мэр,мер
być,to be,быть,бути
całkowicie,completely,полностью,повністю
cały,whole,целый,цілий
cecha,feature,черта,риса
cel,goal,цель,мета
cela,cell,камера,камера
celebryta,celebrity,знаменитость,знаменитість
centrum,center,центр,центр
chemia,chemistry,химия,хімія
chmura,cloud,облако,хмара
chociaż,although,хотя,хоча
choroba,illness,болезнь,хвороба
chory,sick,больной,хворий
chronić,to protect,защищать,захищати
ciągnąć,to pull,тянуть,тягнути
ciastko,cookie,печенье,печиво
cicho,quietly,тихо,тихо
ciemność,darkness,темнота,темрява
cienki,thin,тонкий,тонкий
ciepło,warmth,тепло,тепло
ciężki,heavy,тяжёлый,важкий
ciężko,heavily,тяжело,важко
co,what,что,що
codzienny,daily,ежедневный,щоденний
cukierki,candies,конфеты,цукерки
ćwiczyć,to exercise,тренироваться,тренуватися
cyfrowy,digital,цифровой,цифровий
cykl,cycle,цикл,цикл
cytryna,lemon,лимон,лимон
czarny,black,чёрный,чорний
czekanie,waiting,ожидание,очікування
czerwony,red,красный,червоний
człowiek,person,человек,людина
czubek,tip,кончик,кінчик
czyj,whose,чей,чий
czynnik,factor,фактор,фактор
czyścić,to clean,чистить,чистити
czysty,clean,чистый,чистий
dach,roof,крыша,дах
dalej,further,дальше,далі
daleko,far,далеко,далеко
dane,data,данные,дані
darmowy,free,бесплатный,безкоштовний
decyzja,decision,решение,рішення
dentysta,dentist,стоматолог,стоматолог
departament,department,департамент,департамент
deser,dessert,десерт,десерт
deska,board,доска,дошка
detektyw,detective,детектив,детектив
długo,long,долго,довго
dmuchać,to blow,дуть,дмухати
dno,bottom,дно,дно
dobroczynność,charity,благотворительность,благодійність
dobry,good,хороший,добрий
dobrze,well,хорошо,добре
dokładnie,exactly,точно,точно
dokładny,precise,точный,точний
dokument,document,документ,документ
dolina,valley,долина,долина
dom,home,дом,дім
dorosły,adult,взрослый,дорослий
dość,enough,достаточно,достатньо
doskonały,perfect,отличный,відмінний
dostępny,available,доступный,доступний
doświadczenie,experience,опыт,досвід
dotknąć,to touch,тронуть,торкнутися
dowcip,joke,шутка,жарт
dowód,proof,доказательство,доказ
drama,drama,драма,драма
drewniany,wooden,деревянный,дерев'яний
drewno,wood,древесина,деревина
drugi,second,второй,другий
drukarka,printer,принтер,принтер
drukować,to print,печатать,друкувати
dużo,many,много,багато
dyrektor,director,директор,директор
dyskusja,discussion,дискуссия,дискусія
dywan,carpet,ковёр,килим
dżem,jam,джем,джем
działać,to work,действовать,діяти
dziecko,child,ребёнок,дитина
dziennik,diary / journal,дневник / журнал,щоденник / журнал
dziennikarz,journalist,журналист,журналіст
dziki,wild,дикий,дикий
dzisiaj,today,сегодня,сьогодні
dziura,hole,дыра,діра
dziwny,strange,странный,дивний
dzwonić,to call,звонить,телефонувати
edukacja,education,образование,освіта
efekt,effect,эффект,ефект
ekran,screen,экран,екран
ekspert,expert,эксперт,експерт
eksperyment,experiment,эксперимент,експеримент
ekstremalny,extreme,экстремальный,екстремальний
elektroniczny,electronic,электронный,електронний
elektryczność,electricity,электричество,електрика
elektryczny,electric,электрический,електричний
energia,energy,энергия,енергія
esej,essay,эссе,есе
fabryka,factory,фабрика,фабрика
facet,guy,парень,хлопець
farma,farm,ферма,ферма
fasola,beans,фасоль,квасоля
fikcja,fiction,вымысел,вигадка
film,film,фильм,фільм
finał,final,финал,фінал
fioletowy,purple,фиолетовый,фіолетовий
fizyczny,physical,физический,фізичний
fizyka,physics,физика,фізика
formalny,formal,формальный,формальний
formować,to form,формировать,формувати
fotografować,to photograph,фотографировать,фотографувати
galeria,gallery,галерея,галерея
garnitur,suit,костюм,костюм
gaz,gas,газ,газ
gdzie,where,где,де
gdziekolwiek,anywhere,где угодно,де завгодно
gdzieś,somewhere,где-то,десь
głęboki,deep,глубокий,глибокий
głos,voice,голос,голос
głośno,loudly,громко,голосно
głośny,loud,громкий,голосний
głównie,mainly,в основном,в основному
główny,main,главный,головний
głupi,stupid,глупый,дурний
golf,golf,гольф,гольф
gorączka,fever,лихорадка,гарячка
gorszy,worse,хуже,гірше
gość,guest,гость,гість
gotować,to cook,готовить,готувати
gotówka,cash,наличные,готівка
gra,game,игра,гра
grosz,grosz / penny,грош / копейка,гріш / копійка
gruby,thick,толстый,товстий
grypa,flu,грипп,грип
grzeczny,polite,вежливый,ввічливий
guzik,button,пуговица,гудзик
gwiazda,star,звезда,зірка
hałas,noise,шум,шум
hałaśliwy,noisy,шумный,шумний
harmonogram,schedule,расписание,розклад
hokej,hockey,хоккей,хокей
idealny,perfect,идеальный,ідеальний
ilość,quantity,количество,кількість
inaczej,differently,иначе,інакше
inny,other,другой,інший
instrukcja,instruction,инструкция,інструкція
instruktor,instructor,инструктор,інструктор
instrument,instrument,инструмент,інструмент
inteligentny,intelligent,умный,розумний
interesować,to interest,интересовать,цікавити
inżynier,engineer,инженер,інженер
istnieć,to exist,существовать,існувати
jak,how,как,як
jakość,quality,качество,якість
jasny,bright,светлый,світлий
jazda,driving / ride,езда,їзда
jeden,one,один,один
jednostka,unit,единица,одиниця
jego,his,его,його
jej,her,её,її
jeszcze,still,ещё,ще
jezioro,lake,озеро,озеро
jutro,tomorrow,завтра,завтра
już,already,уже,вже
kamień,stone,камень,камінь
kampus,campus,кампус,кампус
katastrofa,disaster,катастрофа,катастрофа
kawałek,piece,кусок,шматок
każdy,every,каждый,кожен
kiedy,when,когда,коли
kierunek,direction,направление,напрямок
kieszeń,pocket,карман,кишеня
kilka,several,несколько,кілька
kilometr,kilometer,километр,кілометр
klasyczny,classic,классический,класичний
klimat,climate,климат,клімат
kłótnia,argument,ссора,сварка
kluczowy,key,ключевой,ключовий
kod,code,код,код
kolano,knee,колено,коліно
kolega,colleague,коллега,колега
kolej,railway,железная дорога,залізниця
koło,wheel,колесо,колесо
kolumna,column,колонна,колона
komedia,comedy,комедия,комедія
komentarz,comment,комментарий,коментар
kompletny,complete,полный,повний
komunikować,to communicate,общаться,спілкуватися
konferencja,conference,конференция,конференція
koniec,end,конец,кінець
konieczny,necessary,необходимый,необхідний
konkurs,competition,конкурс,конкурс
kontekst,context,контекст,контекст
kontrolować,to control,контролировать,контролювати
kontynent,continent,континент,континент
kontynuować,to continue,продолжать,продовжувати
kopiować,to copy,копировать,копіювати
korzyść,benefit,польза,користь
kość,bone,кость,кістка
kościół,church,церковь,церква
kostka,cube,кубик,кубик
kosztować,to cost,стоить,коштувати
kraść,to steal,красть,красти
kreatywny,creative,творческий,творчий
kręcony,curly,кудрявый,кучерявий
kredyt,loan,кредит,кредит
kreskówka,cartoon,мультфильм,мультфільм
krew,blood,кровь,кров
krok,step,шаг,крок
król,king,король,король
królowa,queen,королева,королева
krzyczeć,to shout,кричать,кричати
księżyc,moon,луна,місяць
kształt,shape,форма,форма
ktokolwiek,anyone,кто угодно,хто завгодно
który,which,который,який
kucharz,cook,повар,кухар
kuchenka,stove,плита,плита
kwadratowy,square,квадратный,квадратний
laboratorium,laboratory,лаборатория,лабораторія
lądować,to land,приземляться,приземлятися
lampa,lamp,лампа,лампа
las,forest,лес,ліс
latanie,flying,полёт,політ
łatwo,easily,легко,легко
lek,medicine,лекарство,ліки
lekarstwo,medicine,лекарство,ліки
lekki,light,лёгкий,легкий
leniwy,lazy,ленивый,лінивий
lewy,left,левый,лівий
liczba,number,число,число
liczyć,to count,считать,рахувати
lider,leader,лидер,лідер
link,link,ссылка,посилання
lodówka,fridge,холодильник,холодильник
luka,gap,пробел,прогалина
lustro,mirror,зеркало,дзеркало
łyżka,spoon,ложка,ложка
machnięcie,wave,взмах,змах
malarz,painter,художник,художник
mało,little,мало,мало
malować,to paint,рисовать,малювати
małpa,monkey,обезьяна,мавпа
matematyka,mathematics,математика,математика
materiał,material,материал,матеріал
meble,furniture,мебель,меблі
media,media,медиа,медіа
medyczny,medical,медицинский,медичний
menedżer,manager,менеджер,менеджер
męski,male,мужской,чоловічий
metal,metal,металл,метал
metoda,method,метод,метод
metro,subway,метро,метро
mieć,to have,иметь,мати
międzynarodowy,international,международный,міжнародний
miejsce,place,место,місце
miękki,soft,мягкий,м'який
miliard,billion,миллиард,мільярд
miłość,love,любовь,любов
minąć,to pass,пройти,минути
miniony,past,прошлый,минулий
miska,bowl,миска,миска
mniej,less,меньше,менше
moda,fashion,мода,мода
modelka,model,модель,модель
mój,my,мой,мій
mokry,wet,мокрый,мокрий
morze,sea,море,море
most,bridge,мост,міст
motocykl,motorcycle,мотоцикл,мотоцикл
mowa,speech,речь,мова
mówca,speaker,оратор,оратор
może,maybe,возможно,можливо
mózg,brain,мозг,мозок
możliwość,possibility,возможность,можливість
mundur,uniform,униформа,уніформа
muzyczny,musical,музыкальный,музичний
muzyk,musician,музыкант,музикант
mycie,washing,мытьё,миття
mydło,soap,мыло,мило
myśl,thought,мысль,думка
myślenie,thinking,мышление,мислення
nad,above,над,над
nadzieja,hope,надежда,надія
nagle,suddenly,вдруг,раптом
nagranie,recording,запись,запис
nagroda,prize,приз,приз
nagrywać,to record,записывать,записувати
najgorszy,worst,худший,найгірший
najlepiej,best,лучше всего,найкраще
najmniej,least,меньше всего,найменше
najwięcej,most,больше всего,найбільше
napój,drink,напиток,напій
naprawić,to repair,починить,полагодити
naprzód,forward,вперёд,вперед
narciarstwo,skiing,лыжный спорт,лижний спорт
narodowy,national,национальный,національний
narodziny,birth,рождение,народження
narzędzie,tool,инструмент,інструмент
narzekać,to complain,жаловаться,скаржитися
następny,next,следующий,наступний
natura,nature,природа,природа
natychmiast,immediately,немедленно,негайно
nauczanie,teaching,преподавание,викладання
nauka,learning,обучение,навчання
nawyk,habit,привычка,звичка
nazywać,to call,называть,називати
nerwowy,nervous,нервный,нервовий
niebezpieczeństwo,danger,опасность,небезпека
niebieski,blue,синий,синій
niebo,sky,небо,небо
niedawno,recently,недавно,нещодавно
niedawny,recent,недавний,нещодавній
niedźwiedź,bear,медведь,ведмідь
nieformalny,informal,неформальный,неформальний
niegrzeczny,impolite,невежливый,неввічливий
niektóre,some,некоторые,деякі
niemożliwy,impossible,невозможный,неможливий
niespodzianka,surprise,сюрприз,сюрприз
niestety,unfortunately,к сожалению,на жаль
nieszczęśliwy,unhappy,несчастный,нещасливий
nietypowy,unusual,необычный,незвичайний
niewiarygodny,incredible,невероятный,неймовірний
niezależny,independent,независимый,незалежний
niezwykle,extremely,чрезвычайно,надзвичайно
nigdzie,nowhere,нигде,ніде
niski,low,низкий,низький
normalnie,normally,обычно,зазвичай
normalny,normal,нормальный,нормальний
nóż,knife,нож,ніж
obietnica,promise,обещание,обіцянка
oblać,to fail,провалить,провалити
obóz,camp,лагерь,табір
obraz,picture,картина,картина
oczekiwać,to expect,ожидать,очікувати
od,from,от,від
oddzielny,separate,отдельный,окремий
odkryć,to discover,обнаружить,виявити
odkrycie,discovery,открытие,відкриття
odległość,distance,расстояние,відстань
odmówić,to refuse,отказать,відмовити
odnosić,to relate,относиться,ставитися
odpoczynek,rest,отдых,відпочинок
odpowiedź,answer,ответ,відповідь
odpowiedzieć,to answer,ответить,відповісти
odzież,clothing,одежда,одяг
oferować,to offer,предлагать,пропонувати
ogólny,general,общий,загальний
ogromny,huge,огромный,величезний
okazja,opportunity,возможность,можливість
około,about,около,близько
okrągły,round,круглый,круглий
okropny,terrible,ужасный,жахливий
olej,oil,масло,олія
oparty,based,основанный,заснований
opcja,option,вариант,варіант
oprócz,except,кроме,крім
organizacja,organization,организация,організація
organizować,to organize,организовывать,організовувати
oryginalny,original,оригинальный,оригінальний
orzech,nut,орех,горіх
osiągnąć,to achieve,достичь,досягти
osobowość,personality,личность,особистість
ostatni,last,последний,останній
ostrożnie,carefully,осторожно,обережно
ostrożny,careful,осторожный,обережний
otrzymać,to receive,получить,отримати
otwarty,open,открытый,відкритий
owad,insect,насекомое,комаха
oznaczyć,to mark,обозначить,позначити
pacjent,patient,пациент,пацієнт
padać,to fall,падать,падати
pająk,spider,паук,павук
pakować,to pack,паковать,пакувати
pałac,palace,дворец,палац
palec,finger,палец,палець
palenie,smoking,курение,куріння
palić,to smoke,курить,курити
pamięć,memory,память,пам'ять
pan,sir,господин,пан
pani,madam,госпожа,пані
papieros,cigarette,сигарета,цигарка
para,pair,пара,пара
parkowanie,parking,парковка,паркування
pasażer,passenger,пассажир,пасажир
pasek,belt,ремень,ремінь
pasować,to fit,подходить,підходити
pchać,to push,толкать,штовхати
pędzel,paintbrush,кисть,пензель
pęk,bundle,связка,в'язка
peron,platform,платформа,платформа
pewien,certain,определённый,певний
piec,to bake,печь,пекти
pierścionek,ring,кольцо,каблучка
pierwszy,first,первый,перший
pilot,remote,пульт,пульт
płaca,wage,зарплата,зарплата
płakać,to cry,плакать,плакати
plakat,poster,плакат,плакат
planeta,planet,планета,планета
planować,to plan,планировать,планувати
płaski,flat,плоский,плаский
plastik,plastic,пластик,пластик
pobrać,to download,скачать,завантажити
pobyt,stay,пребывание,перебування
początek,beginning,начало,початок
poczta,post office,почта,пошта
pod,under,под,під
podczas,during,во время,під час
podnieść,to lift,поднять,підняти
podróż,journey,путешествие,подорож
podróżny,traveler,путешественник,мандрівник
podwójny,double,двойной,подвійний
pojedynczy,single,одиночный,одиничний
pokaz,show,показ,показ
pokój,room,комната,кімната
połączony,connected,соединённый,з'єднаний
połączyć,to connect,соединить,з'єднати
pole,field,поле,поле
polecać,to recommend,рекомендовать,рекомендувати
północ,north,север,північ
połowa,half,половина,половина
południowy,southern,южный,південний
pomarańczowy,orange,оранжевый,помаранчевий
pomiędzy,between,между,між
pomoc,help,помощь,допомога
pomocny,helpful,полезный,корисний
poprawić,to improve,улучшить,покращити
poprawnie,correctly,правильно,правильно
populacja,population,население,населення
posiadać,to own,владеть,володіти
post,post,пост,пост
postać,character,персонаж,персонаж
postęp,progress,прогресс,прогрес
potrzeba,need,потребность,потреба
poważny,serious,серьёзный,серйозний
powieść,novel,роман,роман
powitać,to welcome,приветствовать,вітати
powrót,return,возвращение,повернення
poziom,level,уровень,рівень
później,later,позже,пізніше
pozwalać,to allow,позволять,дозволяти
pozwolenie,permission,разрешение,дозвіл
pozycja,position,позиция,позиція
pożyczyć,to borrow,занять,позичити
praca,work,работа,робота
pracodawca,employer,работодатель,роботодавець
pracownik,employee,работник,працівник
pracujący,working,работающий,працюючий
pranie,laundry,стирка,прання
prawdopodobny,probable,вероятный,ймовірний
prawidłowy,correct,правильный,правильний
prawie,almost,почти,майже
prawo,law,закон,закон
prędkość,speed,скорость,швидкість
prezent,gift,подарок,подарунок
prezentować,to present,представлять,представляти
prezydent,president,президент,президент
problem,problem,проблема,проблема
procent,percent,процент,відсоток
proces,process,процесс,процес
produkować,to produce,производить,виробляти
profesjonalny,professional,профессиональный,професійний
profesor,professor,профессор,професор
profil,profile,профиль,профіль
projektant,designer,дизайнер,дизайнер
projektować,to design,проектировать,проєктувати
propozycja,proposal,предложение,пропозиція
prośba,request,просьба,прохання
prosto,straight,прямо,прямо
prosty,simple,простой,простий
proszę,please,пожалуйста,будь ласка
prowadzić,to drive,водить,керувати
przechodzić,to cross,переходить,переходити
przeciw,against,против,проти
przeciwieństwo,opposite,противоположность,протилежність
przed,before,перед,перед
przedni,front,передний,передній
przedstawienie,performance,представление,вистава
przejażdżka,ride,поездка,поїздка
przemysł,industry,промышленность,промисловість
przepis,recipe,рецепт,рецепт
przerwa,break,перерыв,перерва
prześcieradło,sheet,простыня,простирадло
przestępca,criminal,преступник,злочинець
przestępstwo,crime,преступление,злочин
przestraszony,scared,испуганный,наляканий
przewidzieć,to predict,предвидеть,передбачити
przez,through,через,через
przeziębienie,cold,простуда,застуда
przyczyna,cause,причина,причина
przygoda,adventure,приключение,пригода
przykryć,to cover,накрыть,накрити
przypadek,case,случай,випадок
przystanek,stop,остановка,зупинка
przyszłość,future,будущее,майбутнє
publiczność,audience,аудитория,аудиторія
publikować,to publish,публиковать,публікувати
pukać,to knock,стучать,стукати
pusty,empty,пустой,порожній
pustynia,desert,пустыня,пустеля
pytanie,question,вопрос,питання
raczej,rather,скорее,скоріше
radośnie,joyfully,радостно,радісно
raportować,to report,отчитываться,звітувати
reagować,to react,реагировать,реагувати
recykling,recycling,переработка,переробка
ręcznik,towel,полотенце,рушник
region,region,регион,регіон
regularny,regular,регулярный,регулярний
reklama,advertisement,реклама,реклама
reklamować,to advertise,рекламировать,рекламувати
reporter,reporter,репортёр,репортер
robić,to do,делать,робити
rodzaj,type,тип,тип
rodzina,family,семья,сім'я
róg,corner,угол,кут
rola,role,роль,роль
rolnictwo,agriculture,сельское хозяйство,сільське господарство
rozdawać,to distribute,раздавать,роздавати
rozmawiać,to talk,разговаривать,розмовляти
rozmiar,size,размер,розмір
rozmowa,conversation,разговор,розмова
różnorodność,diversity,разнообразие,різноманітність
różowy,pink,розовый,рожевий
rozpoznać,to recognize,распознать,розпізнати
rozważać,to consider,рассматривать,розглядати
rozwiązać,to solve,решить,вирішити
rozwiązanie,solution,решение,рішення
rozwiedziony,divorced,разведённый,розлучений
rozwijać,to develop,развивать,розвивати
ruch,movement,движение,рух
rysunek,drawing,рисунок,малюнок
rywalizować,to compete,соревноваться,змагатися
rząd,government,правительство,уряд
rzucać,to throw,бросать,кидати
sala,room,зал,зал
sam,alone,один,сам
scena,scene,сцена,сцена
schodek,step,ступенька,сходинка
ścigać,to chase,преследовать,переслідувати
sekret,secret,секрет,секрет
sekretarz,secretary,секретарь,секретар
sen,dream,сон,сон
serce,heart,сердце,серце
seria,series,серия,серія
serwować,to serve,подавать,подавати
siebie,oneself,себя,себе
sieć,network,сеть,мережа
siedzenie,seat,сиденье,сидіння
silnik,engine,двигатель,двигун
skakać,to jump,прыгать,стрибати
skarpetka,sock,носок,шкарпетка
skóra,skin,кожа,шкіра
słaby,weak,слабый,слабкий
słodki,sweet,сладкий,солодкий
ślub,wedding,свадьба,весілля
słuchacz,listener,слушатель,слухач
smak,taste,вкус,смак
śmiech,laughter,смех,сміх
śmieci,trash,мусор,сміття
smutnie,sadly,грустно,сумно
śnić,to dream,сниться,снитися
śnieg,snow,снег,сніг
sos,sauce,соус,соус
spacer,walk,прогулка,прогулянка
specyficzny,specific,специфический,специфічний
śpiący,sleepy,сонный,сонний
śpiew,singing,пение,спів
społeczeństwo,society,общество,суспільство
społeczność,community,сообщество,спільнота
społeczny,social,социальный,соціальний
sportowiec,athlete,спортсмен,спортсмен
sposób,way,способ,спосіб
spóźniony,late,опоздавший,запізнілий
sprawa,matter,дело,справа
sprawdzenie,check,проверка,перевірка
sprawiedliwy,fair,справедливый,справедливий
sprzedaż,sale,продажа,продаж
sprzęt,equipment,оборудование,обладнання
srebro,silver,серебро,срібло
średni,medium,средний,середній
środkowy,middle,средний,середній
środowisko,environment,окружающая среда,довкілля
stan,state,состояние,стан
starożytny,ancient,древний,давній
statek,ship,корабль,корабель
stawka,rate,ставка,ставка
stopień,degree,степень,ступінь
strach,fear,страх,страх
straszny,terrible,ужасный,жахливий
strategia,strategy,стратегия,стратегія
stres,stress,стресс,стрес
struktura,structure,структура,структура
strzelać,to shoot,стрелять,стріляти
suchy,dry,сухой,сухий
sugerować,to suggest,предлагать,пропонувати
święto,holiday,праздник,свято
tablet,tablet,планшет,планшет
tak,yes,да,так
taki,such,такой,такий
talerz,plate,тарелка,тарілка
taniec,dance,танец,танець
technologia,technology,технология,технологія
telefon,phone,телефон,телефон
telefonować,to call,звонить,телефонувати
temperatura,temperature,температура,температура
ten,this,этот,цей
termin,deadline,срок,термін
testować,to test,тестировать,тестувати
tło,background,фон,тло
tłum,crowd,толпа,натовп
tłuszcz,fat,жир,жир
tor,track,трасса,траса
tradycja,tradition,традиция,традиція
tradycyjny,traditional,традиционный,традиційний
trampki,sneakers,кеды,кросівки
transport,transport,транспорт,транспорт
trasa,route,маршрут,маршрут
trawa,grass,трава,трава
trener,coach,тренер,тренер
trenować,to train,тренироваться,тренуватися
trochę,little,немного,трохи
troska,concern,забота,турбота
trząść,to shake,трясти,трясти
trzeci,third,третий,третій
trzymać,to hold,держать,тримати
turystyka,tourism,туризм,туризм
twój,your,твой,твій
tylko,only,только,тільки
typowy,typical,типичный,типовий
ubierać,to dress,одевать,одягати
uczenie,learning,обучение,навчання
uczestniczyć,to participate,участвовать,брати участь
uderzyć,to hit,ударить,вдарити
układ,arrangement,расположение,розташування
układać,to arrange,раскладывать,розкладати
ukryć,to hide,спрятать,сховати
ulubieniec,favorite,любимец,улюбленець
umysł,mind,разум,розум
unikać,to avoid,избегать,уникати
upadek,fall,падение,падіння
upuścić,to drop,уронить,упустити
uraz,injury,травма,травма
urządzenie,device,устройство,пристрій
urzędnik,official,чиновник,чиновник
usługa,service,услуга,послуга
uśmiech,smile,улыбка,усмішка
usunąć,to remove,удалить,видалити
uświadomić,to realize,осознать,усвідомити
uwaga,attention,внимание,увага
użycie,use,использование,використання
użytkownik,user,пользователь,користувач
wąchać,to smell,нюхать,нюхати
waga,weight,вес,вага
walczyć,to fight,сражаться,боротися
wąski,narrow,узкий,вузький
wcale,at all,вовсе,зовсім
wcześnie,early,рано,рано
wczoraj,yesterday,вчера,вчора
wędkarstwo,fishing,рыбалка,рибальство
wejść,to enter,войти,увійти
wiatr,wind,ветер,вітер
widelec,fork,вилка,виделка
widok,view,вид,краєвид
więcej,more,больше,більше
wiedza,knowledge,знание,знання
wiek,age,возраст,вік
wiele,many,много,багато
wieś,village,деревня,село
wieża,tower,башня,вежа
więzienie,prison,тюрьма,в'язниця
winda,elevator,лифт,ліфт
wirus,virus,вирус,вірус
witaj,hello,здравствуй,вітаю
wizyta,visit,визит,візит
władza,power,власть,влада
właściciel,owner,владелец,власник
właściwie,actually,собственно,власне
własność,property,собственность,власність
wliczony,included,включённый,включений
wokoło,around,вокруг,навколо
wolno,allowed,разрешено,дозволено
wołowina,beef,говядина,яловичина
wow,wow,вау,вау
wpływać,to affect,влиять,впливати
wschodni,eastern,восточный,східний
wspierać,to support,поддерживать,підтримувати
wspomnieć,to mention,упомянуть,згадати
wśród,among,среди,серед
wszędzie,everywhere,везде,скрізь
wszystko,everything,всё,все
wybierać,to choose,выбирать,обирати
wybór,choice,выбор,вибір
wybrzeże,coast,побережье,узбережжя
wycieczka,trip,поездка,поїздка
wygląd,appearance,внешность,зовнішність
wygodny,comfortable,удобный,зручний
wyjaśnienie,explanation,объяснение,пояснення
wykład,lecture,лекция,лекція
wykonać,to perform,выполнить,виконати
wymawiać,to pronounce,произносить,вимовляти
wynagrodzenie,salary,зарплата,заробітна плата
wynalazek,invention,изобретение,винахід
wynaleźć,to invent,изобрести,винайти
wypadek,accident,авария,аварія
wypisać,to write out,выписать,виписати
wyraz,word,слово,слово
wyrazić,to express,выразить,висловити
wyraźnie,clearly,ясно,чітко
wysłać,to send,отправить,надіслати
wysoki,high,высокий,високий
wysokość,height,высота,висота
wystarczająco,enough,достаточно,достатньо
wzdłuż,along,вдоль,уздовж
wzgórze,hill,холм,пагорб
wznosić,to raise,поднимать,піднімати
wzór,pattern,узор,візерунок
zaangażować,to involve,вовлечь,залучити
zaatakować,to attack,атаковать,атакувати
zabawka,toy,игрушка,іграшка
zabawny,funny,смешной,смішний
zachodni,western,западный,західний
zachowanie,behavior,поведение,поведінка
zadanie,task,задание,завдання
żaden,none,никакой,жоден
zadowolony,satisfied,довольный,задоволений
zagraniczny,foreign,иностранный,іноземний
zagubiony,lost,потерянный,загублений
zakładać,to assume,предполагать,припускати
zakończenie,ending,окончание,закінчення
zakończyć,to finish,закончить,закінчити
zakręt,turn,поворот,поворот
zaleta,advantage,преимущество,перевага
zależeć,to depend,зависеть,залежати
zamiast,instead,вместо,замість
zamknąć,to close,закрыть,закрити
zamknięty,closed,закрытый,закритий
zamówić,to order,заказать,замовити
zanieczyszczenie,pollution,загрязнение,забруднення
zapałka,match,спичка,сірник
zaparkować,to park,припарковать,припаркувати
zapewnić,to ensure,обеспечить,забезпечити
zapisać,to save,сохранить,зберегти
zapobiegać,to prevent,предотвращать,запобігати
zaprosić,to invite,пригласить,запросити
zaproszenie,invitation,приглашение,запрошення
zarabiać,to earn,зарабатывать,заробляти
zarezerwować,to book,забронировать,забронювати
zarządzać,to manage,управлять,керувати
zaskakujący,surprising,удивительный,дивовижний
zastąpić,to replace,заменить,замінити
zatłoczony,crowded,переполненный,переповнений
zatrudniać,to employ,нанимать,наймати
zauważyć,to notice,заметить,помітити
zawierać,to contain,содержать,містити
zbierać,to collect,собирать,збирати
zdecydowanie,definitely,определённо,безумовно
zdobyć,to gain,получить,здобути
zdolność,ability,способность,здатність
zdolny,able,способный,здатний
zdziwiony,surprised,удивлённый,здивований
zegarek,watch,часы,годинник
żeglarstwo,sailing,парусный спорт,вітрильний спорт
żeglować,to sail,плавать,плавати
żeński,female,женский,жіночий
zepsuty,broken,сломанный,зламаний
zero,zero,ноль,нуль
zgadywanie,guessing,угадывание,вгадування
zidentyfikować,to identify,идентифицировать,ідентифікувати
zielony,green,зелёный,зелений
ziemia,earth / soil,земля / почва,земля / ґрунт
zjednoczony,united,объединённый,об'єднаний
złapać,to catch,поймать,спіймати
źle,badly,плохо,погано
złodziej,thief,вор,злодій
złoto,gold,золото,золото
zmartwiony,worried,обеспокоенный,стурбований
zmiana,change,изменение,зміна
zmniejszyć,to reduce,уменьшить,зменшити
zmysł,sense,чувство,відчуття
znaczek,stamp,марка,марка
znak,sign,знак,знак
zniknąć,to disappear,исчезнуть,зникнути
zniszczyć,to destroy,уничтожить,знищити
żołądek,stomach,желудок,шлунок
żołnierz,soldier,солдат,солдат
żółty,yellow,жёлтый,жовтий
zranić,to hurt,ранить,поранити
źródło,source,источник,джерело
zrozumienie,understanding,понимание,розуміння
związać,to tie,связать,зв'язати
związek,relationship,отношения,стосунки
zwiększyć,to increase,увеличить,збільшити
zwycięzca,winner,победитель,переможець
zwyczajny,ordinary,обычный,звичайний
zwykły,usual,обычный,звичайний
życzyć,to wish,желать,бажати
żywy,alive,живой,живий
1 polish english russian ukrainian
2 adwokat lawyer адвокат адвокат
3 akceptuję I accept я принимаю я приймаю
4 aktywny active активный активний
5 albo or или або
6 alternatywa alternative альтернатива альтернатива
7 analizować to analyze анализировать аналізувати
8 aplikacja application приложение додаток
9 aplikować to apply подавать заявление подавати заяву
10 architekt architect архитектор архітектор
11 architektura architecture архитектура архітектура
12 armia army армия армія
13 asystent assistant ассистент асистент
14 atrakcyjny attractive привлекательный привабливий
15 autor author автор автор
16 as much as / until аж / пока аж / поки
17 badacz researcher исследователь дослідник
18 badania research исследования дослідження
19 badanie examination / study обследование / исследование обстеження / дослідження
20 bezpieczny safe безопасный безпечний
21 bezpośredni direct прямой прямий
22 biały white белый білий
23 bić to beat бить бити
24 bieg run / race бег / забег біг / забіг
25 biegacz runner бегун бігун
26 bieganie running бег біг
27 biologia biology биология біологія
28 biznesmen businessman бизнесмен бізнесмен
29 biżuteria jewelry ювелирные изделия ювелірні вироби
30 błąd mistake ошибка помилка
31 blisko near близко близько
32 bliźniak twin близнец близнюк
33 blok block блок блок
34 błyskotliwy brilliant блестящий блискучий
35 bóg god бог бог
36 bohater hero герой герой
37 bok side бок бік
38 ból pain боль біль
39 brakujący missing отсутствующий відсутній
40 brama gate ворота ворота
41 brązowy brown коричневый коричневий
42 brzmieć to sound звучать звучати
43 burmistrz mayor мэр мер
44 być to be быть бути
45 całkowicie completely полностью повністю
46 cały whole целый цілий
47 cecha feature черта риса
48 cel goal цель мета
49 cela cell камера камера
50 celebryta celebrity знаменитость знаменитість
51 centrum center центр центр
52 chemia chemistry химия хімія
53 chmura cloud облако хмара
54 chociaż although хотя хоча
55 choroba illness болезнь хвороба
56 chory sick больной хворий
57 chronić to protect защищать захищати
58 ciągnąć to pull тянуть тягнути
59 ciastko cookie печенье печиво
60 cicho quietly тихо тихо
61 ciemność darkness темнота темрява
62 cienki thin тонкий тонкий
63 ciepło warmth тепло тепло
64 ciężki heavy тяжёлый важкий
65 ciężko heavily тяжело важко
66 co what что що
67 codzienny daily ежедневный щоденний
68 cukierki candies конфеты цукерки
69 ćwiczyć to exercise тренироваться тренуватися
70 cyfrowy digital цифровой цифровий
71 cykl cycle цикл цикл
72 cytryna lemon лимон лимон
73 czarny black чёрный чорний
74 czekanie waiting ожидание очікування
75 czerwony red красный червоний
76 człowiek person человек людина
77 czubek tip кончик кінчик
78 czyj whose чей чий
79 czynnik factor фактор фактор
80 czyścić to clean чистить чистити
81 czysty clean чистый чистий
82 dach roof крыша дах
83 dalej further дальше далі
84 daleko far далеко далеко
85 dane data данные дані
86 darmowy free бесплатный безкоштовний
87 decyzja decision решение рішення
88 dentysta dentist стоматолог стоматолог
89 departament department департамент департамент
90 deser dessert десерт десерт
91 deska board доска дошка
92 detektyw detective детектив детектив
93 długo long долго довго
94 dmuchać to blow дуть дмухати
95 dno bottom дно дно
96 dobroczynność charity благотворительность благодійність
97 dobry good хороший добрий
98 dobrze well хорошо добре
99 dokładnie exactly точно точно
100 dokładny precise точный точний
101 dokument document документ документ
102 dolina valley долина долина
103 dom home дом дім
104 dorosły adult взрослый дорослий
105 dość enough достаточно достатньо
106 doskonały perfect отличный відмінний
107 dostępny available доступный доступний
108 doświadczenie experience опыт досвід
109 dotknąć to touch тронуть торкнутися
110 dowcip joke шутка жарт
111 dowód proof доказательство доказ
112 drama drama драма драма
113 drewniany wooden деревянный дерев'яний
114 drewno wood древесина деревина
115 drugi second второй другий
116 drukarka printer принтер принтер
117 drukować to print печатать друкувати
118 dużo many много багато
119 dyrektor director директор директор
120 dyskusja discussion дискуссия дискусія
121 dywan carpet ковёр килим
122 dżem jam джем джем
123 działać to work действовать діяти
124 dziecko child ребёнок дитина
125 dziennik diary / journal дневник / журнал щоденник / журнал
126 dziennikarz journalist журналист журналіст
127 dziki wild дикий дикий
128 dzisiaj today сегодня сьогодні
129 dziura hole дыра діра
130 dziwny strange странный дивний
131 dzwonić to call звонить телефонувати
132 edukacja education образование освіта
133 efekt effect эффект ефект
134 ekran screen экран екран
135 ekspert expert эксперт експерт
136 eksperyment experiment эксперимент експеримент
137 ekstremalny extreme экстремальный екстремальний
138 elektroniczny electronic электронный електронний
139 elektryczność electricity электричество електрика
140 elektryczny electric электрический електричний
141 energia energy энергия енергія
142 esej essay эссе есе
143 fabryka factory фабрика фабрика
144 facet guy парень хлопець
145 farma farm ферма ферма
146 fasola beans фасоль квасоля
147 fikcja fiction вымысел вигадка
148 film film фильм фільм
149 finał final финал фінал
150 fioletowy purple фиолетовый фіолетовий
151 fizyczny physical физический фізичний
152 fizyka physics физика фізика
153 formalny formal формальный формальний
154 formować to form формировать формувати
155 fotografować to photograph фотографировать фотографувати
156 galeria gallery галерея галерея
157 garnitur suit костюм костюм
158 gaz gas газ газ
159 gdzie where где де
160 gdziekolwiek anywhere где угодно де завгодно
161 gdzieś somewhere где-то десь
162 głęboki deep глубокий глибокий
163 głos voice голос голос
164 głośno loudly громко голосно
165 głośny loud громкий голосний
166 głównie mainly в основном в основному
167 główny main главный головний
168 głupi stupid глупый дурний
169 golf golf гольф гольф
170 gorączka fever лихорадка гарячка
171 gorszy worse хуже гірше
172 gość guest гость гість
173 gotować to cook готовить готувати
174 gotówka cash наличные готівка
175 gra game игра гра
176 grosz grosz / penny грош / копейка гріш / копійка
177 gruby thick толстый товстий
178 grypa flu грипп грип
179 grzeczny polite вежливый ввічливий
180 guzik button пуговица гудзик
181 gwiazda star звезда зірка
182 hałas noise шум шум
183 hałaśliwy noisy шумный шумний
184 harmonogram schedule расписание розклад
185 hokej hockey хоккей хокей
186 idealny perfect идеальный ідеальний
187 ilość quantity количество кількість
188 inaczej differently иначе інакше
189 inny other другой інший
190 instrukcja instruction инструкция інструкція
191 instruktor instructor инструктор інструктор
192 instrument instrument инструмент інструмент
193 inteligentny intelligent умный розумний
194 interesować to interest интересовать цікавити
195 inżynier engineer инженер інженер
196 istnieć to exist существовать існувати
197 jak how как як
198 jakość quality качество якість
199 jasny bright светлый світлий
200 jazda driving / ride езда їзда
201 jeden one один один
202 jednostka unit единица одиниця
203 jego his его його
204 jej her её її
205 jeszcze still ещё ще
206 jezioro lake озеро озеро
207 jutro tomorrow завтра завтра
208 już already уже вже
209 kamień stone камень камінь
210 kampus campus кампус кампус
211 katastrofa disaster катастрофа катастрофа
212 kawałek piece кусок шматок
213 każdy every каждый кожен
214 kiedy when когда коли
215 kierunek direction направление напрямок
216 kieszeń pocket карман кишеня
217 kilka several несколько кілька
218 kilometr kilometer километр кілометр
219 klasyczny classic классический класичний
220 klimat climate климат клімат
221 kłótnia argument ссора сварка
222 kluczowy key ключевой ключовий
223 kod code код код
224 kolano knee колено коліно
225 kolega colleague коллега колега
226 kolej railway железная дорога залізниця
227 koło wheel колесо колесо
228 kolumna column колонна колона
229 komedia comedy комедия комедія
230 komentarz comment комментарий коментар
231 kompletny complete полный повний
232 komunikować to communicate общаться спілкуватися
233 konferencja conference конференция конференція
234 koniec end конец кінець
235 konieczny necessary необходимый необхідний
236 konkurs competition конкурс конкурс
237 kontekst context контекст контекст
238 kontrolować to control контролировать контролювати
239 kontynent continent континент континент
240 kontynuować to continue продолжать продовжувати
241 kopiować to copy копировать копіювати
242 korzyść benefit польза користь
243 kość bone кость кістка
244 kościół church церковь церква
245 kostka cube кубик кубик
246 kosztować to cost стоить коштувати
247 kraść to steal красть красти
248 kreatywny creative творческий творчий
249 kręcony curly кудрявый кучерявий
250 kredyt loan кредит кредит
251 kreskówka cartoon мультфильм мультфільм
252 krew blood кровь кров
253 krok step шаг крок
254 król king король король
255 królowa queen королева королева
256 krzyczeć to shout кричать кричати
257 księżyc moon луна місяць
258 kształt shape форма форма
259 ktokolwiek anyone кто угодно хто завгодно
260 który which который який
261 kucharz cook повар кухар
262 kuchenka stove плита плита
263 kwadratowy square квадратный квадратний
264 laboratorium laboratory лаборатория лабораторія
265 lądować to land приземляться приземлятися
266 lampa lamp лампа лампа
267 las forest лес ліс
268 latanie flying полёт політ
269 łatwo easily легко легко
270 lek medicine лекарство ліки
271 lekarstwo medicine лекарство ліки
272 lekki light лёгкий легкий
273 leniwy lazy ленивый лінивий
274 lewy left левый лівий
275 liczba number число число
276 liczyć to count считать рахувати
277 lider leader лидер лідер
278 link link ссылка посилання
279 lodówka fridge холодильник холодильник
280 luka gap пробел прогалина
281 lustro mirror зеркало дзеркало
282 łyżka spoon ложка ложка
283 machnięcie wave взмах змах
284 malarz painter художник художник
285 mało little мало мало
286 malować to paint рисовать малювати
287 małpa monkey обезьяна мавпа
288 matematyka mathematics математика математика
289 materiał material материал матеріал
290 meble furniture мебель меблі
291 media media медиа медіа
292 medyczny medical медицинский медичний
293 menedżer manager менеджер менеджер
294 męski male мужской чоловічий
295 metal metal металл метал
296 metoda method метод метод
297 metro subway метро метро
298 mieć to have иметь мати
299 międzynarodowy international международный міжнародний
300 miejsce place место місце
301 miękki soft мягкий м'який
302 miliard billion миллиард мільярд
303 miłość love любовь любов
304 minąć to pass пройти минути
305 miniony past прошлый минулий
306 miska bowl миска миска
307 mniej less меньше менше
308 moda fashion мода мода
309 modelka model модель модель
310 mój my мой мій
311 mokry wet мокрый мокрий
312 morze sea море море
313 most bridge мост міст
314 motocykl motorcycle мотоцикл мотоцикл
315 mowa speech речь мова
316 mówca speaker оратор оратор
317 może maybe возможно можливо
318 mózg brain мозг мозок
319 możliwość possibility возможность можливість
320 mundur uniform униформа уніформа
321 muzyczny musical музыкальный музичний
322 muzyk musician музыкант музикант
323 mycie washing мытьё миття
324 mydło soap мыло мило
325 myśl thought мысль думка
326 myślenie thinking мышление мислення
327 nad above над над
328 nadzieja hope надежда надія
329 nagle suddenly вдруг раптом
330 nagranie recording запись запис
331 nagroda prize приз приз
332 nagrywać to record записывать записувати
333 najgorszy worst худший найгірший
334 najlepiej best лучше всего найкраще
335 najmniej least меньше всего найменше
336 najwięcej most больше всего найбільше
337 napój drink напиток напій
338 naprawić to repair починить полагодити
339 naprzód forward вперёд вперед
340 narciarstwo skiing лыжный спорт лижний спорт
341 narodowy national национальный національний
342 narodziny birth рождение народження
343 narzędzie tool инструмент інструмент
344 narzekać to complain жаловаться скаржитися
345 następny next следующий наступний
346 natura nature природа природа
347 natychmiast immediately немедленно негайно
348 nauczanie teaching преподавание викладання
349 nauka learning обучение навчання
350 nawyk habit привычка звичка
351 nazywać to call называть називати
352 nerwowy nervous нервный нервовий
353 niebezpieczeństwo danger опасность небезпека
354 niebieski blue синий синій
355 niebo sky небо небо
356 niedawno recently недавно нещодавно
357 niedawny recent недавний нещодавній
358 niedźwiedź bear медведь ведмідь
359 nieformalny informal неформальный неформальний
360 niegrzeczny impolite невежливый неввічливий
361 niektóre some некоторые деякі
362 niemożliwy impossible невозможный неможливий
363 niespodzianka surprise сюрприз сюрприз
364 niestety unfortunately к сожалению на жаль
365 nieszczęśliwy unhappy несчастный нещасливий
366 nietypowy unusual необычный незвичайний
367 niewiarygodny incredible невероятный неймовірний
368 niezależny independent независимый незалежний
369 niezwykle extremely чрезвычайно надзвичайно
370 nigdzie nowhere нигде ніде
371 niski low низкий низький
372 normalnie normally обычно зазвичай
373 normalny normal нормальный нормальний
374 nóż knife нож ніж
375 obietnica promise обещание обіцянка
376 oblać to fail провалить провалити
377 obóz camp лагерь табір
378 obraz picture картина картина
379 oczekiwać to expect ожидать очікувати
380 od from от від
381 oddzielny separate отдельный окремий
382 odkryć to discover обнаружить виявити
383 odkrycie discovery открытие відкриття
384 odległość distance расстояние відстань
385 odmówić to refuse отказать відмовити
386 odnosić to relate относиться ставитися
387 odpoczynek rest отдых відпочинок
388 odpowiedź answer ответ відповідь
389 odpowiedzieć to answer ответить відповісти
390 odzież clothing одежда одяг
391 oferować to offer предлагать пропонувати
392 ogólny general общий загальний
393 ogromny huge огромный величезний
394 okazja opportunity возможность можливість
395 około about около близько
396 okrągły round круглый круглий
397 okropny terrible ужасный жахливий
398 olej oil масло олія
399 oparty based основанный заснований
400 opcja option вариант варіант
401 oprócz except кроме крім
402 organizacja organization организация організація
403 organizować to organize организовывать організовувати
404 oryginalny original оригинальный оригінальний
405 orzech nut орех горіх
406 osiągnąć to achieve достичь досягти
407 osobowość personality личность особистість
408 ostatni last последний останній
409 ostrożnie carefully осторожно обережно
410 ostrożny careful осторожный обережний
411 otrzymać to receive получить отримати
412 otwarty open открытый відкритий
413 owad insect насекомое комаха
414 oznaczyć to mark обозначить позначити
415 pacjent patient пациент пацієнт
416 padać to fall падать падати
417 pająk spider паук павук
418 pakować to pack паковать пакувати
419 pałac palace дворец палац
420 palec finger палец палець
421 palenie smoking курение куріння
422 palić to smoke курить курити
423 pamięć memory память пам'ять
424 pan sir господин пан
425 pani madam госпожа пані
426 papieros cigarette сигарета цигарка
427 para pair пара пара
428 parkowanie parking парковка паркування
429 pasażer passenger пассажир пасажир
430 pasek belt ремень ремінь
431 pasować to fit подходить підходити
432 pchać to push толкать штовхати
433 pędzel paintbrush кисть пензель
434 pęk bundle связка в'язка
435 peron platform платформа платформа
436 pewien certain определённый певний
437 piec to bake печь пекти
438 pierścionek ring кольцо каблучка
439 pierwszy first первый перший
440 pilot remote пульт пульт
441 płaca wage зарплата зарплата
442 płakać to cry плакать плакати
443 plakat poster плакат плакат
444 planeta planet планета планета
445 planować to plan планировать планувати
446 płaski flat плоский плаский
447 plastik plastic пластик пластик
448 pobrać to download скачать завантажити
449 pobyt stay пребывание перебування
450 początek beginning начало початок
451 poczta post office почта пошта
452 pod under под під
453 podczas during во время під час
454 podnieść to lift поднять підняти
455 podróż journey путешествие подорож
456 podróżny traveler путешественник мандрівник
457 podwójny double двойной подвійний
458 pojedynczy single одиночный одиничний
459 pokaz show показ показ
460 pokój room комната кімната
461 połączony connected соединённый з'єднаний
462 połączyć to connect соединить з'єднати
463 pole field поле поле
464 polecać to recommend рекомендовать рекомендувати
465 północ north север північ
466 połowa half половина половина
467 południowy southern южный південний
468 pomarańczowy orange оранжевый помаранчевий
469 pomiędzy between между між
470 pomoc help помощь допомога
471 pomocny helpful полезный корисний
472 poprawić to improve улучшить покращити
473 poprawnie correctly правильно правильно
474 populacja population население населення
475 posiadać to own владеть володіти
476 post post пост пост
477 postać character персонаж персонаж
478 postęp progress прогресс прогрес
479 potrzeba need потребность потреба
480 poważny serious серьёзный серйозний
481 powieść novel роман роман
482 powitać to welcome приветствовать вітати
483 powrót return возвращение повернення
484 poziom level уровень рівень
485 później later позже пізніше
486 pozwalać to allow позволять дозволяти
487 pozwolenie permission разрешение дозвіл
488 pozycja position позиция позиція
489 pożyczyć to borrow занять позичити
490 praca work работа робота
491 pracodawca employer работодатель роботодавець
492 pracownik employee работник працівник
493 pracujący working работающий працюючий
494 pranie laundry стирка прання
495 prawdopodobny probable вероятный ймовірний
496 prawidłowy correct правильный правильний
497 prawie almost почти майже
498 prawo law закон закон
499 prędkość speed скорость швидкість
500 prezent gift подарок подарунок
501 prezentować to present представлять представляти
502 prezydent president президент президент
503 problem problem проблема проблема
504 procent percent процент відсоток
505 proces process процесс процес
506 produkować to produce производить виробляти
507 profesjonalny professional профессиональный професійний
508 profesor professor профессор професор
509 profil profile профиль профіль
510 projektant designer дизайнер дизайнер
511 projektować to design проектировать проєктувати
512 propozycja proposal предложение пропозиція
513 prośba request просьба прохання
514 prosto straight прямо прямо
515 prosty simple простой простий
516 proszę please пожалуйста будь ласка
517 prowadzić to drive водить керувати
518 przechodzić to cross переходить переходити
519 przeciw against против проти
520 przeciwieństwo opposite противоположность протилежність
521 przed before перед перед
522 przedni front передний передній
523 przedstawienie performance представление вистава
524 przejażdżka ride поездка поїздка
525 przemysł industry промышленность промисловість
526 przepis recipe рецепт рецепт
527 przerwa break перерыв перерва
528 prześcieradło sheet простыня простирадло
529 przestępca criminal преступник злочинець
530 przestępstwo crime преступление злочин
531 przestraszony scared испуганный наляканий
532 przewidzieć to predict предвидеть передбачити
533 przez through через через
534 przeziębienie cold простуда застуда
535 przyczyna cause причина причина
536 przygoda adventure приключение пригода
537 przykryć to cover накрыть накрити
538 przypadek case случай випадок
539 przystanek stop остановка зупинка
540 przyszłość future будущее майбутнє
541 publiczność audience аудитория аудиторія
542 publikować to publish публиковать публікувати
543 pukać to knock стучать стукати
544 pusty empty пустой порожній
545 pustynia desert пустыня пустеля
546 pytanie question вопрос питання
547 raczej rather скорее скоріше
548 radośnie joyfully радостно радісно
549 raportować to report отчитываться звітувати
550 reagować to react реагировать реагувати
551 recykling recycling переработка переробка
552 ręcznik towel полотенце рушник
553 region region регион регіон
554 regularny regular регулярный регулярний
555 reklama advertisement реклама реклама
556 reklamować to advertise рекламировать рекламувати
557 reporter reporter репортёр репортер
558 robić to do делать робити
559 rodzaj type тип тип
560 rodzina family семья сім'я
561 róg corner угол кут
562 rola role роль роль
563 rolnictwo agriculture сельское хозяйство сільське господарство
564 rozdawać to distribute раздавать роздавати
565 rozmawiać to talk разговаривать розмовляти
566 rozmiar size размер розмір
567 rozmowa conversation разговор розмова
568 różnorodność diversity разнообразие різноманітність
569 różowy pink розовый рожевий
570 rozpoznać to recognize распознать розпізнати
571 rozważać to consider рассматривать розглядати
572 rozwiązać to solve решить вирішити
573 rozwiązanie solution решение рішення
574 rozwiedziony divorced разведённый розлучений
575 rozwijać to develop развивать розвивати
576 ruch movement движение рух
577 rysunek drawing рисунок малюнок
578 rywalizować to compete соревноваться змагатися
579 rząd government правительство уряд
580 rzucać to throw бросать кидати
581 sala room зал зал
582 sam alone один сам
583 scena scene сцена сцена
584 schodek step ступенька сходинка
585 ścigać to chase преследовать переслідувати
586 sekret secret секрет секрет
587 sekretarz secretary секретарь секретар
588 sen dream сон сон
589 serce heart сердце серце
590 seria series серия серія
591 serwować to serve подавать подавати
592 siebie oneself себя себе
593 sieć network сеть мережа
594 siedzenie seat сиденье сидіння
595 silnik engine двигатель двигун
596 skakać to jump прыгать стрибати
597 skarpetka sock носок шкарпетка
598 skóra skin кожа шкіра
599 słaby weak слабый слабкий
600 słodki sweet сладкий солодкий
601 ślub wedding свадьба весілля
602 słuchacz listener слушатель слухач
603 smak taste вкус смак
604 śmiech laughter смех сміх
605 śmieci trash мусор сміття
606 smutnie sadly грустно сумно
607 śnić to dream сниться снитися
608 śnieg snow снег сніг
609 sos sauce соус соус
610 spacer walk прогулка прогулянка
611 specyficzny specific специфический специфічний
612 śpiący sleepy сонный сонний
613 śpiew singing пение спів
614 społeczeństwo society общество суспільство
615 społeczność community сообщество спільнота
616 społeczny social социальный соціальний
617 sportowiec athlete спортсмен спортсмен
618 sposób way способ спосіб
619 spóźniony late опоздавший запізнілий
620 sprawa matter дело справа
621 sprawdzenie check проверка перевірка
622 sprawiedliwy fair справедливый справедливий
623 sprzedaż sale продажа продаж
624 sprzęt equipment оборудование обладнання
625 srebro silver серебро срібло
626 średni medium средний середній
627 środkowy middle средний середній
628 środowisko environment окружающая среда довкілля
629 stan state состояние стан
630 starożytny ancient древний давній
631 statek ship корабль корабель
632 stawka rate ставка ставка
633 stopień degree степень ступінь
634 strach fear страх страх
635 straszny terrible ужасный жахливий
636 strategia strategy стратегия стратегія
637 stres stress стресс стрес
638 struktura structure структура структура
639 strzelać to shoot стрелять стріляти
640 suchy dry сухой сухий
641 sugerować to suggest предлагать пропонувати
642 święto holiday праздник свято
643 tablet tablet планшет планшет
644 tak yes да так
645 taki such такой такий
646 talerz plate тарелка тарілка
647 taniec dance танец танець
648 technologia technology технология технологія
649 telefon phone телефон телефон
650 telefonować to call звонить телефонувати
651 temperatura temperature температура температура
652 ten this этот цей
653 termin deadline срок термін
654 testować to test тестировать тестувати
655 tło background фон тло
656 tłum crowd толпа натовп
657 tłuszcz fat жир жир
658 tor track трасса траса
659 tradycja tradition традиция традиція
660 tradycyjny traditional традиционный традиційний
661 trampki sneakers кеды кросівки
662 transport transport транспорт транспорт
663 trasa route маршрут маршрут
664 trawa grass трава трава
665 trener coach тренер тренер
666 trenować to train тренироваться тренуватися
667 trochę little немного трохи
668 troska concern забота турбота
669 trząść to shake трясти трясти
670 trzeci third третий третій
671 trzymać to hold держать тримати
672 turystyka tourism туризм туризм
673 twój your твой твій
674 tylko only только тільки
675 typowy typical типичный типовий
676 ubierać to dress одевать одягати
677 uczenie learning обучение навчання
678 uczestniczyć to participate участвовать брати участь
679 uderzyć to hit ударить вдарити
680 układ arrangement расположение розташування
681 układać to arrange раскладывать розкладати
682 ukryć to hide спрятать сховати
683 ulubieniec favorite любимец улюбленець
684 umysł mind разум розум
685 unikać to avoid избегать уникати
686 upadek fall падение падіння
687 upuścić to drop уронить упустити
688 uraz injury травма травма
689 urządzenie device устройство пристрій
690 urzędnik official чиновник чиновник
691 usługa service услуга послуга
692 uśmiech smile улыбка усмішка
693 usunąć to remove удалить видалити
694 uświadomić to realize осознать усвідомити
695 uwaga attention внимание увага
696 użycie use использование використання
697 użytkownik user пользователь користувач
698 wąchać to smell нюхать нюхати
699 waga weight вес вага
700 walczyć to fight сражаться боротися
701 wąski narrow узкий вузький
702 wcale at all вовсе зовсім
703 wcześnie early рано рано
704 wczoraj yesterday вчера вчора
705 wędkarstwo fishing рыбалка рибальство
706 wejść to enter войти увійти
707 wiatr wind ветер вітер
708 widelec fork вилка виделка
709 widok view вид краєвид
710 więcej more больше більше
711 wiedza knowledge знание знання
712 wiek age возраст вік
713 wiele many много багато
714 wieś village деревня село
715 wieża tower башня вежа
716 więzienie prison тюрьма в'язниця
717 winda elevator лифт ліфт
718 wirus virus вирус вірус
719 witaj hello здравствуй вітаю
720 wizyta visit визит візит
721 władza power власть влада
722 właściciel owner владелец власник
723 właściwie actually собственно власне
724 własność property собственность власність
725 wliczony included включённый включений
726 wokoło around вокруг навколо
727 wolno allowed разрешено дозволено
728 wołowina beef говядина яловичина
729 wow wow вау вау
730 wpływać to affect влиять впливати
731 wschodni eastern восточный східний
732 wspierać to support поддерживать підтримувати
733 wspomnieć to mention упомянуть згадати
734 wśród among среди серед
735 wszędzie everywhere везде скрізь
736 wszystko everything всё все
737 wybierać to choose выбирать обирати
738 wybór choice выбор вибір
739 wybrzeże coast побережье узбережжя
740 wycieczka trip поездка поїздка
741 wygląd appearance внешность зовнішність
742 wygodny comfortable удобный зручний
743 wyjaśnienie explanation объяснение пояснення
744 wykład lecture лекция лекція
745 wykonać to perform выполнить виконати
746 wymawiać to pronounce произносить вимовляти
747 wynagrodzenie salary зарплата заробітна плата
748 wynalazek invention изобретение винахід
749 wynaleźć to invent изобрести винайти
750 wypadek accident авария аварія
751 wypisać to write out выписать виписати
752 wyraz word слово слово
753 wyrazić to express выразить висловити
754 wyraźnie clearly ясно чітко
755 wysłać to send отправить надіслати
756 wysoki high высокий високий
757 wysokość height высота висота
758 wystarczająco enough достаточно достатньо
759 wzdłuż along вдоль уздовж
760 wzgórze hill холм пагорб
761 wznosić to raise поднимать піднімати
762 wzór pattern узор візерунок
763 zaangażować to involve вовлечь залучити
764 zaatakować to attack атаковать атакувати
765 zabawka toy игрушка іграшка
766 zabawny funny смешной смішний
767 zachodni western западный західний
768 zachowanie behavior поведение поведінка
769 zadanie task задание завдання
770 żaden none никакой жоден
771 zadowolony satisfied довольный задоволений
772 zagraniczny foreign иностранный іноземний
773 zagubiony lost потерянный загублений
774 zakładać to assume предполагать припускати
775 zakończenie ending окончание закінчення
776 zakończyć to finish закончить закінчити
777 zakręt turn поворот поворот
778 zaleta advantage преимущество перевага
779 zależeć to depend зависеть залежати
780 zamiast instead вместо замість
781 zamknąć to close закрыть закрити
782 zamknięty closed закрытый закритий
783 zamówić to order заказать замовити
784 zanieczyszczenie pollution загрязнение забруднення
785 zapałka match спичка сірник
786 zaparkować to park припарковать припаркувати
787 zapewnić to ensure обеспечить забезпечити
788 zapisać to save сохранить зберегти
789 zapobiegać to prevent предотвращать запобігати
790 zaprosić to invite пригласить запросити
791 zaproszenie invitation приглашение запрошення
792 zarabiać to earn зарабатывать заробляти
793 zarezerwować to book забронировать забронювати
794 zarządzać to manage управлять керувати
795 zaskakujący surprising удивительный дивовижний
796 zastąpić to replace заменить замінити
797 zatłoczony crowded переполненный переповнений
798 zatrudniać to employ нанимать наймати
799 zauważyć to notice заметить помітити
800 zawierać to contain содержать містити
801 zbierać to collect собирать збирати
802 zdecydowanie definitely определённо безумовно
803 zdobyć to gain получить здобути
804 zdolność ability способность здатність
805 zdolny able способный здатний
806 zdziwiony surprised удивлённый здивований
807 zegarek watch часы годинник
808 żeglarstwo sailing парусный спорт вітрильний спорт
809 żeglować to sail плавать плавати
810 żeński female женский жіночий
811 zepsuty broken сломанный зламаний
812 zero zero ноль нуль
813 zgadywanie guessing угадывание вгадування
814 zidentyfikować to identify идентифицировать ідентифікувати
815 zielony green зелёный зелений
816 ziemia earth / soil земля / почва земля / ґрунт
817 zjednoczony united объединённый об'єднаний
818 złapać to catch поймать спіймати
819 źle badly плохо погано
820 złodziej thief вор злодій
821 złoto gold золото золото
822 zmartwiony worried обеспокоенный стурбований
823 zmiana change изменение зміна
824 zmniejszyć to reduce уменьшить зменшити
825 zmysł sense чувство відчуття
826 znaczek stamp марка марка
827 znak sign знак знак
828 zniknąć to disappear исчезнуть зникнути
829 zniszczyć to destroy уничтожить знищити
830 żołądek stomach желудок шлунок
831 żołnierz soldier солдат солдат
832 żółty yellow жёлтый жовтий
833 zranić to hurt ранить поранити
834 źródło source источник джерело
835 zrozumienie understanding понимание розуміння
836 związać to tie связать зв'язати
837 związek relationship отношения стосунки
838 zwiększyć to increase увеличить збільшити
839 zwycięzca winner победитель переможець
840 zwyczajny ordinary обычный звичайний
841 zwykły usual обычный звичайний
842 życzyć to wish желать бажати
843 żywy alive живой живий
+848
View File
@@ -0,0 +1,848 @@
polish,english,russian,ukrainian
absolutnie,absolutely,абсолютно,абсолютно
administracja,administration,администрация,адміністрація
agent,agent,агент,агент
akademicki,academic,академический,академічний
aktualizować,to update,обновлять,оновлювати
alarm,alarm,сигнализация,сигналізація
albo,or,или,або
album,album,альбом,альбом
alternatywny,alternative,альтернативный,альтернативний
ambicja,ambition,амбиция,амбіція
analiza,analysis,анализ,аналіз
ani,neither / nor,ни,ні
aplikacja,application,приложение,додаток
asortyment,assortment,ассортимент,асортимент
asystent,assistant,ассистент,асистент
atak,attack,атака,атака
atmosfera,atmosphere,атмосфера,атмосфера
atrakcja,attraction,аттракцион,атракціон
automatycznie,automatically,автоматически,автоматично
automatyczny,automatic,автоматический,автоматичний
autorytet,authority,авторитет,авторитет
autostrada,highway,автострада,автострада
bąbel,bubble,пузырь,бульбашка
badać,to research,исследовать,досліджувати
bałagan,mess,беспорядок,безлад
bardzo,very,очень,дуже
bateria,battery,батарея,батарея
bawełna,cotton,хлопок,бавовна
bęben,drum,барабан,барабан
bezpieczeństwo,safety,безопасность,безпека
bezpośrednio,directly,непосредственно,безпосередньо
bezrobocie,unemployment,безработица,безробіття
bezrobotny,unemployed,безработный,безробітний
bieda,poverty,бедность,бідність
bielizna,underwear,нижнее бельё,нижня білизна
bieżący,current,текущий,поточний
bitwa,battle,битва,битва
biwakować,to camp,ходить в поход,ходити в похід
blady,pale,бледный,блідий
blisko,near,близко,близько
bliźniak,twin,близнец,близнюк
blokować,to block,блокировать,блокувати
błoto,mud,грязь,багнюка
błyszczący,shiny,блестящий,блискучий
bójka,fight,драка,бійка
bolesny,painful,болезненный,болісний
brakować,to lack,не хватать,бракувати
brzeg,shore,берег,берег
brzydki,ugly,некрасивый,негарний
burza,storm,гроза,гроза
całkowicie,completely,полностью,повністю
całkowity,total,полный,повний
całość,whole,целое,ціле
całować,to kiss,целовать,цілувати
cel,goal,цель,мета
celować,to aim,целиться,цілитися
cenny,valuable,ценный,цінний
centralny,central,центральный,центральний
centrować,to center,центрировать,центрувати
centrum,center,центр,центр
ceremonia,ceremony,церемония,церемонія
chociaż,although,хотя,хоча
ciągły,continuous,непрерывный,безперервний
ciągnięcie,pulling,тяга,тягнення
ciasny,tight,тесный,тісний
cichy,quiet,тихий,тихий
ciecz,liquid,жидкость,рідина
cierpieć,to suffer,страдать,страждати
ciśnienie,pressure,давление,тиск
codziennie,daily,ежедневно,щоденно
cokolwiek,anything,что угодно,що завгодно
cytat,quote,цитата,цитата
cytować,to quote,цитировать,цитувати
czapka,cap,шапка,шапка
często,often,часто,часто
czy,whether,ли,чи
czyj,whose,чей,чий
czyn,deed,поступок,вчинок
dalej,further,дальше,далі
daleki,far,далёкий,далекий
definicja,definition,определение,визначення
definiować,to define,определять,визначати
definitywny,definitive,окончательный,остаточний
dekada,decade,десятилетие,десятиліття
diament,diamond,алмаз,діамант
długość,length,длина,довжина
dno,bottom,дно,дно
doceniać,to appreciate,ценить,цінувати
dodanie,addition,добавление,додавання
dodatkowy,additional,дополнительный,додатковий
dokument,document,документ,документ
dołączyć,to join,присоединиться,приєднатися
dom,home,дом,дім
doradzać,to advise,советовать,радити
doskonale,perfectly,отлично,відмінно
dostarczać,to deliver,доставлять,доставляти
dostarczyć,to deliver,доставить,доставити
dostęp,access,доступ,доступ
doświadczenie,experience,опыт,досвід
doświadczony,experienced,опытный,досвідчений
dotyk,touch,прикосновение,дотик
do tyłu,backwards,назад,назад
drugi,second,второй,другий
drugorzędny,secondary,второстепенный,другорядний
druk,printing,печать,друк
drżenie,trembling,дрожь,тремтіння
duch,spirit,дух,дух
dumny,proud,гордый,гордий
dużo,many,много,багато
dym,smoke,дым,дим
dyrektor,director,директор,директор
dżentelmen,gentleman,джентльмен,джентльмен
działanie,action,действие,дія
dzieciństwo,childhood,детство,дитинство
dzielnica,district,район,район
dziennik,diary,дневник,щоденник
dziwny,strange,странный,дивний
dzwon,bell,колокол,дзвін
dzwonić,to call,звонить,телефонувати
edukacyjny,educational,образовательный,освітній
edukować,to educate,обучать,навчати
efektywnie,effectively,эффективно,ефективно
ekonomiczny,economic,экономический,економічний
ekscytacja,excitement,волнение,хвилювання
ekspert,expert,эксперт,експерт
eksperymentować,to experiment,экспериментировать,експериментувати
eksplodować,to explode,взрываться,вибухати
eksplozja,explosion,взрыв,вибух
eksport,export,экспорт,експорт
elegancki,elegant,элегантный,елегантний
element,element,элемент,елемент
emeryt,retiree,пенсионер,пенсіонер
emocja,emotion,эмоция,емоція
etykieta,label,этикетка,етикетка
ewentualnie,possibly,возможно,можливо
fabuła,plot,сюжет,сюжет
fascynujący,fascinating,захватывающий,захопливий
federalny,federal,федеральный,федеральний
filmować,to film,снимать,знімати
finansowy,financial,финансовый,фінансовий
flaga,flag,флаг,прапор
fokus,focus,фокус,фокус
fotograf,photographer,фотограф,фотограф
fotografia,photography,фотография,фотографія
funkcja,function,функция,функція
futro,fur,мех,хутро
gałąź,branch,ветка,гілка
garaż,garage,гараж,гараж
gardło,throat,горло,горло
garnek,pot,кастрюля,каструля
gdziekolwiek,anywhere,где угодно,де завгодно
gdzieś,somewhere,где-то,десь
gigantyczny,gigantic,гигантский,гігантський
gładki,smooth,гладкий,гладкий
gleba,soil,почва,ґрунт
głęboki,deep,глубокий,глибокий
globalny,global,глобальный,глобальний
głośny,loud,громкий,голосний
głosować,to vote,голосовать,голосувати
głównie,mainly,в основном,в основному
głupi,stupid,глупый,дурний
gorzej,worse,хуже,гірше
gospodarka,economy,экономика,економіка
gospodarz,host,хозяин,господар
granica,border,граница,кордон
groza,horror,ужас,жах
gryźć,to bite,кусать,кусати
gwałtowny,violent,резкий,різкий
gwiazda,star,звезда,зірка
gwóźdź,nail,гвоздь,цвях
handlować,to trade,торговать,торгувати
historyczny,historical,исторический,історичний
hojny,generous,щедрый,щедрий
huragan,hurricane,ураган,ураган
ich,their,их,їх
igła,needle,игла,голка
ignorować,to ignore,игнорировать,ігнорувати
imigrant,immigrant,иммигрант,іммігрант
imponujący,impressive,впечатляющий,вражаючий
import,import,импорт,імпорт
indywidualny,individual,индивидуальный,індивідуальний
inteligencja,intelligence,интеллект,інтелект
intencja,intention,намерение,намір
inwestować,to invest,инвестировать,інвестувати
inżynieria,engineering,инженерия,інженерія
irytujący,irritating,раздражающий,дратівливий
iść,to go,идти,йти
jądrowy,nuclear,ядерный,ядерний
jakikolwiek,any,любой,будь-який
jasny,bright,светлый,світлий
jeden,one,один,один
język,language,язык,мова
kabel,cable,кабель,кабель
kampania,campaign,кампания,кампанія
kanał,channel,канал,канал
kandydat,candidate,кандидат,кандидат
kapitan,captain,капитан,капітан
kara,punishment,наказание,покарання
kategoria,category,категория,категорія
każdy,every,каждый,кожен
kiedy,when,когда,коли
kiedykolwiek,ever,когда-либо,коли-небудь
kilka,several,несколько,кілька
kłamstwo,lie,ложь,брехня
klauzula,clause,пункт,пункт
klawiatura,keyboard,клавиатура,клавіатура
klient,client,клиент,клієнт
kliknąć,to click,кликнуть,клацнути
klucz,key,ключ,ключ
kobieta,woman,женщина,жінка
kolekcja,collection,коллекция,колекція
kolorowy,colorful,цветной,кольоровий
komentować,to comment,комментировать,коментувати
komercyjny,commercial,коммерческий,комерційний
komunikacja,communication,коммуникация,комунікація
koncentrować,to concentrate,концентрировать,концентрувати
koniecznie,necessarily,обязательно,обов'язково
konkretnie,specifically,конкретно,конкретно
konkurencyjny,competitive,конкурентоспособный,конкурентоспроможний
konkurent,competitor,конкурент,конкурент
konsekwencja,consequence,последствие,наслідок
konsument,consumer,потребитель,споживач
kontakt,contact,контакт,контакт
konto,account,счёт,рахунок
kontrast,contrast,контраст,контраст
kontrola,control,контроль,контроль
kopalnia,mine,шахта,шахта
kopia,copy,копия,копія
kopnąć,to kick,пнуть,копнути
korzystać,to use,пользоваться,користуватися
kostium,costume,костюм,костюм
krawat,tie,галстук,краватка
krawędź,edge,край,край
krążyć,to circulate,циркулировать,циркулювати
kremowy,creamy,кремовый,кремовий
krewny,relative,родственник,родич
królewski,royal,королевский,королівський
kropla,drop,капля,крапля
kryminalny,criminal,криминальный,кримінальний
krzyk,scream,крик,крик
krzyż,cross,крест,хрест
ksiądz,priest,священник,священник
książę,prince,князь,князь
księżniczka,princess,принцесса,принцеса
kukurydza,corn,кукуруза,кукурудза
kulturalny,cultural,культурный,культурний
kurz,dust,пыль,пил
kwadrat,square,квадрат,квадрат
kwalifikacja,qualification,квалификация,кваліфікація
kwalifikować,to qualify,квалифицировать,кваліфікувати
laboratorium,laboratory,лаборатория,лабораторія
lać,to pour,лить,лити
łączyć,to connect,соединять,з'єднувати
łagodny,gentle,мягкий,м'який
łańcuch,chain,цепь,ланцюг
latający,flying,летающий,літаючий
leczenie,treatment,лечение,лікування
ledwie,barely,едва,ледве
lepszy,better,лучший,кращий
liczba,number,число,число
limit,limit,лимит,ліміт
lina,rope,верёвка,мотузка
liść,leaf,лист,лист
literatura,literature,литература,література
lokalizacja,location,местоположение,місцезнаходження
ludzie,people,люди,люди
ludzki,human,человеческий,людський
luksus,luxury,роскошь,розкіш
machnąć,to wave,махнуть,махнути
magia,magic,магия,магія
mąka,flour,мука,борошно
mało,little,мало,мало
mały,small,маленький,малий
małżeństwo,marriage,брак,шлюб
marka,brand,бренд,бренд
marketing,marketing,маркетинг,маркетинг
marnować,to waste,тратить впустую,витрачати даремно
martwić,to worry,беспокоить,хвилювати
martwienie,worrying,беспокойство,хвилювання
marynarz,sailor,моряк,моряк
mentalny,mental,ментальный,ментальний
mężczyzna,man,мужчина,чоловік
miejscowy,local,местный,місцевий
mierzyć,to measure,измерять,вимірювати
mięsień,muscle,мышца,м'яз
mieszać,to mix,мешать,змішувати
mieszanka,mixture,смесь,суміш
mistrz,master,мастер,майстер
młodość,youth,молодость,молодість
młody,young,молодой,молодий
mniej,less,меньше,менше
mocno,strongly,сильно,сильно
modlić,to pray,молиться,молитися
modlitwa,prayer,молитва,молитва
modny,fashionable,модный,модний
moneta,coin,монета,монета
morderstwo,murder,убийство,вбивство
mówiony,spoken,разговорный,розмовний
muszla,shell,ракушка,мушля
nagle,suddenly,вдруг,раптом
nagłówek,headline,заголовок,заголовок
nagranie,recording,запись,запис
najbardziej,most,больше всего,найбільше
najgorszy,worst,худший,найгірший
najlepszy,best,лучший,найкращий
najmniej,least,меньше всего,найменше
najnowszy,newest,новейший,найновіший
najwyższy,highest,высочайший,найвищий
namiot,tent,палатка,намет
napisany,written,написанный,написаний
napiwek,tip,чаевые,чайові
naprawa,repair,ремонт,ремонт
naprzeciw,opposite,напротив,навпроти
naród,nation,нация,нація
narracja,narration,повествование,оповідь
narta,ski,лыжа,лижа
nasionko,seed,семечко,насіння
nastawienie,attitude,настрой,налаштування
następny,next,следующий,наступний
nastrój,mood,настроение,настрій
nasz,our,наш,наш
naturalnie,naturally,естественно,природно
natychmiastowy,immediate,немедленный,негайний
naukowy,scientific,научный,науковий
niedbały,negligent,небрежный,недбалий
niedogodność,inconvenience,неудобство,незручність
nielegalny,illegal,нелегальный,нелегальний
nienawiść,hatred,ненависть,ненависть
niepotrzebny,unnecessary,ненужный,непотрібний
nieprzyjemny,unpleasant,неприятный,неприємний
nieśmiały,shy,застенчивый,сором'язливий
niesprawiedliwy,unfair,несправедливый,несправедливий
niewiarygodnie,incredibly,невероятно,неймовірно
niewinny,innocent,невинный,невинний
niewolnik,slave,раб,раб
niewygodny,uncomfortable,неудобный,незручний
niezbędny,essential,необходимый,необхідний
niezdolny,incapable,неспособный,нездатний
niski,low,низкий,низький
normalny,normal,нормальный,нормальний
obcy,foreign,чужой,чужий
obecnie,currently,в настоящее время,наразі
obecny,present,настоящий,теперішній
obietnica,promise,обещание,обіцянка
objaw,symptom,симптом,симптом
obok,next to,рядом,поруч
obowiązek,duty,обязанность,обов'язок
ocena,rating,оценка,оцінка
oceniać,to rate,оценивать,оцінювати
oczekiwany,expected,ожидаемый,очікуваний
oczywiście,of course,конечно,звичайно
oczywisty,obvious,очевидный,очевидний
od,from,от,від
odcinek,episode,эпизод,епізод
oddech,breath,дыхание,дихання
oddychać,to breathe,дышать,дихати
oddychanie,breathing,дыхание,дихання
oddzielić,to separate,отделить,відокремити
odjazd,departure,отъезд,від'їзд
odległy,distant,далёкий,далекий
odpoczynek,rest,отдых,відпочинок
odpowiedni,suitable,подходящий,відповідний
odpowiedź,answer,ответ,відповідь
odpowiedzialność,responsibility,ответственность,відповідальність
odpowiedzialny,responsible,ответственный,відповідальний
odrzucać,to reject,отклонять,відхиляти
odważny,brave,смелый,сміливий
odzwierciedlać,to reflect,отражать,відображати
oferta,offer,предложение,пропозиція
ofiara,victim,жертва,жертва
ofiarować,to offer,предлагать,пропонувати
oficjalny,official,официальный,офіційний
oglądać,to watch,смотреть,дивитися
ogłosić,to announce,объявить,оголосити
ogłoszenie,announcement,объявление,оголошення
ogólnoświatowy,worldwide,всемирный,всесвітній
ogon,tail,хвост,хвіст
ogrzewanie,heating,отопление,опалення
okazja,opportunity,возможность,можливість
okładka,cover,обложка,обкладинка
około,about,около,близько
określić,to define,определить,визначити
okropny,terrible,ужасный,жахливий
okrutny,cruel,жестокий,жорстокий
ołów,lead,свинец,свинець
opieka,care,забота,турбота
opłata,fee,плата,плата
opona,tire,шина,шина
oprócz,except,кроме,крім
oprogramowanie,software,программное обеспечение,програмне забезпечення
opróżnić,to empty,опустошить,спорожнити
organizator,organizer,организатор,організатор
oryginał,original,оригинал,оригінал
osiągnięcie,achievement,достижение,досягнення
osobiście,personally,лично,особисто
osobno,separately,отдельно,окремо
ostry,sharp,острый,гострий
ostrzegać,to warn,предупреждать,попереджати
ostrzeżenie,warning,предупреждение,попередження
oszukać,to cheat,обмануть,обдурити
oszukiwać,to deceive,обманывать,обманювати
paczka,package,посылка,посилка
palec,finger,палец,палець
paliwo,fuel,топливо,паливо
paragon,receipt,чек,чек
pasja,passion,страсть,пристрасть
pasować,to fit,подходить,підходити
patelnia,frying pan,сковорода,сковорода
pchnięcie,push,толчок,поштовх
personel,staff,персонал,персонал
piasek,sand,песок,пісок
piec,oven,печь,піч
piękno,beauty,красота,краса
pierwotnie,originally,изначально,спочатку
pierwotny,original,первоначальный,первісний
pierwszy,first,первый,перший
pijany,drunk,пьяный,п'яний
pikantny,spicy,острый,гострий
pisać,to write,писать,писати
plama,stain,пятно,пляма
planowanie,planning,планирование,планування
plasterek,slice,ломтик,скибка
plastikowy,plastic,пластиковый,пластиковий
płatność,payment,оплата,оплата
płeć,gender,пол,стать
plik,file,файл,файл
płot,fence,забор,паркан
plus,plus,плюс,плюс
płynąć,to flow,плыть,плисти
pływanie,swimming,плавание,плавання
po,after,после,після
pobieranie,downloading,скачивание,завантаження
pochować,to bury,похоронить,поховати
podać,to pass,подать,подати
podatek,tax,налог,податок
podczas,during,во время,під час
podgrzewać,to heat,подогревать,підігрівати
podkreślać,to emphasize,подчёркивать,підкреслювати
podlewać,to water,поливать,поливати
podobieństwo,similarity,сходство,схожість
podobnie,similarly,похоже,схоже
podobny,similar,похожий,схожий
podpisać,to sign,подписать,підписати
podróż,journey,путешествие,подорож
podstawa,basis,основа,основа
podstawowy,basic,основной,основний
podsumować,to summarize,подытожить,підсумувати
podwójnie,doubly,вдвойне,подвійно
podwyżka,raise,повышение,підвищення
podzielić,to divide,разделить,розділити
podziemny,underground,подземный,підземний
podziwiać,to admire,восхищаться,захоплюватися
poeta,poet,поэт,поет
poezja,poetry,поэзия,поезія
pojedynczy,single,одиночный,одиничний
pojemnik,container,контейнер,контейнер
pokazać,to show,показать,показати
pokierować,to direct,направить,спрямувати
pokolenie,generation,поколение,покоління
pokryty,covered,покрытый,покритий
połączenie,connection,соединение,з'єднання
połączyć,to connect,соединить,з'єднати
polityczny,political,политический,політичний
polityk,politician,политик,політик
polityka,politics,политика,політика
półka,shelf,полка,полиця
północ,north,север,північ
północny,northern,северный,північний
połowa,half,половина,половина
polować,to hunt,охотиться,полювати
położony,located,расположенный,розташований
położyć,to put,положить,покласти
południe,south,юг,південь
południowy,southern,южный,південний
pomimo,despite,несмотря на,незважаючи на
pomnik,monument,памятник,пам'ятник
pomóc,to help,помочь,допомогти
pop,pop,поп,поп
poprawa,improvement,улучшение,покращення
poprawnie,correctly,правильно,правильно
poprzedni,previous,предыдущий,попередній
porównanie,comparison,сравнение,порівняння
port,port,порт,порт
portret,portrait,портрет,портрет
porządkowy,ordinal,порядковый,порядковий
pośpiech,hurry,спешка,поспіх
pośredni,indirect,косвенный,непрямий
poszukiwanie,search,поиск,пошук
potężny,powerful,могущественный,могутній
potrzebować,to need,нуждаться,потребувати
potwierdzić,to confirm,подтвердить,підтвердити
poważnie,seriously,серьёзно,серйозно
powiązany,related,связанный,пов'язаний
powierzchnia,surface,поверхность,поверхня
powiesić,to hang,повесить,повісити
powinien,should,должен,повинен
powtarzany,repeated,повторяемый,повторюваний
powtórzenie,repetition,повторение,повторення
poziomy,horizontal,горизонтальный,горизонтальний
pozostać,to remain,остаться,залишитися
praktyczny,practical,практичный,практичний
prasa,press,пресса,преса
prawda,truth,правда,правда
prawidłowo,correctly,правильно,правильно
prawny,legal,правовой,правовий
prezentacja,presentation,презентация,презентація
prezentować,to present,представлять,представляти
próba,attempt,попытка,спроба
próbka,sample,образец,зразок
problem,problem,проблема,проблема
procent,percent,процент,відсоток
producent,producer,производитель,виробник
produkcja,production,производство,виробництво
prognoza,forecast,прогноз,прогноз
programować,to program,программировать,програмувати
promować,to promote,продвигать,просувати
prosić,to ask,просить,просити
prosty,simple,простой,простий
proszek,powder,порошок,порошок
protest,protest,протест,протест
prywatny,private,частный,приватний
przechowywać,to store,хранить,зберігати
przed,before,перед,перед
przedstawienie,performance,представление,вистава
przekonać,to convince,убедить,переконати
przełączyć,to switch,переключить,перемкнути
przeprosić,to apologize,извиниться,вибачитися
przerażający,terrifying,ужасающий,жахливий
przestraszony,scared,испуганный,наляканий
przestraszyć,to scare,испугать,налякати
przeszkadzać,to disturb,мешать,заважати
przeszły,past,прошлый,минулий
przetłumaczyć,to translate,перевести,перекласти
przewodnik,guide,гид,гід
przez,through,через,через
przeżyć,to survive,пережить,пережити
przybycie,arrival,прибытие,прибуття
przyciągać,to attract,привлекать,приваблювати
przygotowany,prepared,подготовленный,підготовлений
przyjaźń,friendship,дружба,дружба
przyjemność,pleasure,удовольствие,задоволення
przyjemny,pleasant,приятный,приємний
przykleić,to stick,приклеить,приклеїти
przypominać,to remind,напоминать,нагадувати
przysługa,favor,услуга,послуга
przyznać,to admit,признать,визнати
pszczoła,bee,пчела,бджола
publiczny,public,публичный,публічний
pukanie,knocking,стук,стук
pusty,empty,пустой,порожній
ramka,frame,рамка,рамка
ranny,wounded,раненый,поранений
rasa,breed,порода,порода
reakcja,reaction,реакция,реакція
recenzja,review,обзор,огляд
redaktor,editor,редактор,редактор
referencja,reference,референция,референція
regularnie,regularly,регулярно,регулярно
rękawiczka,glove,перчатка,рукавичка
relacja,relation,отношение,відношення
relaksujący,relaxing,расслабляющий,розслаблюючий
religia,religion,религия,релігія
religijny,religious,религиозный,релігійний
reprezentować,to represent,представлять,представляти
rezerwacja,reservation,бронирование,бронювання
robot,robot,робот,робот
rodzimy,native,родной,рідний
romantyczny,romantic,романтический,романтичний
również,also,также,також
równowaga,balance,равновесие,рівновага
równy,equal,равный,рівний
rozcięcie,cut,разрез,розріз
rozczarowany,disappointed,разочарованный,розчарований
rozczarowujący,disappointing,разочаровывающий,розчаровуючий
rozdział,chapter,глава,розділ
rozmowa,conversation,разговор,розмова
różny,different,разный,різний
rozprzestrzeniać,to spread,распространять,поширювати
rozrywka,entertainment,развлечение,розвага
rozsądny,reasonable,разумный,розумний
rozszerzać,to expand,расширять,розширювати
rozwój,development,развитие,розвиток
ruch,movement,движение,рух
rura,pipe,труба,труба
ryzyko,risk,риск,ризик
rząd,government,правительство,уряд
rzadki,rare,редкий,рідкісний
rzadko,rarely,редко,рідко
rządzić,to rule,править,правити
rzeczy,things,вещи,речі
rzeczywiście,indeed,действительно,дійсно
rzeczywistość,reality,реальность,реальність
rzeźba,sculpture,скульптура,скульптура
sąd,court,суд,суд
sam,alone,один,сам
samotny,lonely,одинокий,самотній
scenariusz,script,сценарий,сценарій
schłodzić,to cool,охладить,охолодити
ścieżka,path,тропинка,стежка
sekret,secret,секрет,секрет
sen,dream,сон,сон
serce,heart,сердце,серце
sieć,network,сеть,мережа
siła,strength,сила,сила
skała,rock,скала,скеля
skanować,to scan,сканировать,сканувати
skarga,complaint,жалоба,скарга
składać,to compose,составлять,складати
skoczyć,to jump,прыгнуть,стрибнути
skok,jump,прыжок,стрибок
skóra,skin,кожа,шкіра
skrzydło,wing,крыло,крило
skuteczny,effective,эффективный,ефективний
słodycz,sweetness,сладость,солодкість
sługa,servant,слуга,слуга
smakować,to taste,пробовать,куштувати
smażyć,to fry,жарить,смажити
śmigłowiec,helicopter,вертолёт,гелікоптер
sortować,to sort,сортировать,сортувати
spokój,calm,спокойствие,спокій
spokojny,calm,спокойный,спокійний
spotkanie,meeting,встреча,зустріч
spowodować,to cause,вызвать,спричинити
spowolnić,to slow down,замедлить,уповільнити
spożywać,to consume,потреблять,споживати
sprawiedliwie,fairly,справедливо,справедливо
sprytny,clever,хитрый,хитрий
srebrny,silver,серебряный,срібний
średni,medium,средний,середній
średnia,average,среднее,середнє
środek,middle,середина,середина
środowiskowy,environmental,экологический,екологічний
stadion,stadium,стадион,стадіон
stały,constant,постоянный,постійний
standard,standard,стандарт,стандарт
staromodny,old-fashioned,старомодный,старомодний
statystyka,statistics,статистика,статистика
strata,loss,потеря,втрата
strażnik,guard,охранник,охоронець
streszczenie,summary,резюме,резюме
strzelać,to shoot,стрелять,стріляти
studio,studio,студия,студія
stwierdzić,to state,заявить,заявити
substancja,substance,вещество,речовина
sufit,ceiling,потолок,стеля
suszyć,to dry,сушить,сушити
świadomy,aware,осознанный,свідомий
świecić,to shine,светить,світити
święto,holiday,праздник,свято
sygnalizować,to signal,сигнализировать,сигналізувати
szacunek,respect,уважение,повага
szafka,cabinet,шкафчик,шафка
szalony,crazy,сумасшедший,божевільний
szczególnie,especially,особенно,особливо
szczęście,happiness,счастье,щастя
szczotkować,to brush,чистить щёткой,чистити щіткою
szkoda,pity,жаль,шкода
sznurek,string,верёвка,мотузка
szorstki,rough,шероховатый,шорсткий
szpilka,pin,булавка,шпилька
tajemnica,secret,тайна,таємниця
tak,yes,да,так
taki,such,такой,такий
talent,talent,талант,талант
tani,cheap,дешёвый,дешевий
taśma,tape,лента,стрічка
techniczny,technical,технический,технічний
technika,technique,техника,техніка
temat,topic,тема,тема
teoria,theory,теория,теорія
teraz,now,сейчас,зараз
tkanina,fabric,ткань,тканина
tłumaczenie,translation,перевод,переклад
to,this,это,це
toczyć,to roll,катить,котити
tona,ton,тонна,тонна
tonąć,to drown,тонуть,тонути
tożsamość,identity,идентичность,ідентичність
transportować,to transport,транспортировать,транспортувати
trend,trend,тренд,тренд
trener,coach,тренер,тренер
trochę,little,немного,трохи
troszczyć,to care,заботиться,піклуватися
trucizna,poison,яд,отрута
trudność,difficulty,трудность,трудність
trujący,poisonous,ядовитый,отруйний
trwać,to last,длиться,тривати
twierdzić,to claim,утверждать,стверджувати
typowo,typically,типично,типово
ubrany,dressed,одетый,одягнений
uciec,to escape,убежать,втекти
uczciwy,honest,честный,чесний
uczestniczyć,to participate,участвовать,брати участь
udawać,to pretend,притворяться,прикидатися
udowodnić,to prove,доказать,довести
udział,participation,участие,участь
ukarać,to punish,наказать,покарати
ukończyć,to complete,закончить,закінчити
umieścić,to place,разместить,розмістити
umowa,contract,договор,договір
uprzątnąć,to clean up,убрать,прибрати
uśmiech,smile,улыбка,усмішка
ustawienie,setting,настройка,налаштування
utalentowany,talented,талантливый,талановитий
uwolnić,to release,освободить,звільнити
używany,used,подержанный,вживаний
walka,fight,борьба,боротьба
waluta,currency,валюта,валюта
warga,lip,губа,губа
warstwa,layer,слой,шар
wartość,value,ценность,цінність
wartościowy,valuable,ценный,цінний
wątpliwość,doubt,сомнение,сумнів
ważność,importance,важность,важливість
ważyć,to weigh,взвешивать,зважувати
wcześniej,earlier,раньше,раніше
wdzięczny,grateful,благодарный,вдячний
węgiel,coal,уголь,вугілля
wejście,entrance,вход,вхід
wełna,wool,шерсть,вовна
wersja,version,версия,версія
wesoły,cheerful,весёлый,веселий
wewnątrz,inside,внутри,всередині
wewnętrzny,internal,внутренний,внутрішній
wiara,faith,вера,віра
wiarygodny,credible,достоверный,достовірний
wiązać,to tie,связывать,зв'язувати
widok,view,вид,краєвид
widz,viewer,зритель,глядач
więcej,more,больше,більше
wiersz,poem,стихотворение,вірш
wieś,village,деревня,село
więzień,prisoner,заключённый,в'язень
winny,guilty,виновный,винний
wiodący,leading,ведущий,провідний
własność,property,собственность,власність
własny,own,собственный,власний
wokół,around,вокруг,навколо
wola,will,воля,воля
wolontariusz,volunteer,волонтёр,волонтер
wpływ,influence,влияние,вплив
wrażenie,impression,впечатление,враження
wróg,enemy,враг,ворог
wschód,east,восток,схід
wschodni,eastern,восточный,східний
wsiąść,to get on,сесть,сісти
wskazać,to indicate,указать,вказати
wskazówka,hint,подсказка,підказка
wsparcie,support,поддержка,підтримка
wspomnieć,to mention,упомянуть,згадати
wszyscy,everyone,все,усі
wybory,election,выборы,вибори
wydatki,expenses,расходы,витрати
wygodny,comfortable,удобный,зручний
wyimaginowany,imaginary,воображаемый,уявний
wyjście,exit,выход,вихід
wykład,lecture,лекция,лекція
wykształcony,educated,образованный,освічений
wykwalifikowany,qualified,квалифицированный,кваліфікований
wymagać,to require,требовать,вимагати
wymiana,exchange,обмен,обмін
wynająć,to rent,арендовать,орендувати
wynik,result,результат,результат
wyróżnić,to distinguish,выделить,виділити
wyścigi,racing,гонки,перегони
wysiłek,effort,усилие,зусилля
wysłać,to send,отправить,надіслати
wystąpić,to appear,выступить,виступити
wystarczająco,enough,достаточно,достатньо
wystawa,exhibition,выставка,виставка
wyzwanie,challenge,вызов,виклик
wzdłuż,along,вдоль,уздовж
wzrost,growth,рост,зріст
zaangażowany,engaged,вовлечённый,залучений
zaawansowany,advanced,продвинутый,просунутий
zabawiać,to entertain,развлекать,розважати
zabawny,funny,смешной,смішний
zabójstwo,murder,убийство,вбивство
zachęcać,to encourage,поощрять,заохочувати
zachód,west,запад,захід
zachodni,western,западный,західний
zadanie,task,задание,завдання
zadowolony,satisfied,довольный,задоволений
zafundować,to treat,угостить,пригостити
żagiel,sail,парус,вітрило
zakazać,to forbid,запретить,заборонити
zakończenie,ending,окончание,закінчення
zakończyć,to finish,закончить,закінчити
zalewać,to flood,заливать,заливати
zaliczenie,credit,зачёт,залік
zamarznięty,frozen,замёрзший,замерзлий
zamek,castle,замок,замок
zamierzać,to intend,намереваться,мати намір
zamknięty,closed,закрытый,закритий
zamrozić,to freeze,заморозить,заморозити
zapach,smell,запах,запах
zapał,enthusiasm,энтузиазм,ентузіазм
zapiąć,to fasten,застегнуть,застебнути
żart,joke,шутка,жарт
zarządzanie,management,управление,управління
zaskoczyć,to surprise,удивить,здивувати
zasłona,curtain,занавеска,завіса
zasób,resource,ресурс,ресурс
zatrudnić,to hire,нанять,найняти
zatrudnienie,employment,трудоустройство,працевлаштування
zauważyć,to notice,заметить,помітити
zawartość,content,содержимое,вміст
zawód,profession,профессия,професія
zawstydzony,embarrassed,смущённый,збентежений
zazwyczaj,usually,обычно,зазвичай
zbierać,to collect,собирать,збирати
zdecydowany,determined,решительный,рішучий
zdezorientować,to disorient,дезориентировать,дезорієнтувати
zdezorientowany,confused,растерянный,розгублений
zdumiony,amazed,изумлённый,здивований
żelazo,iron,железо,залізо
żenujący,embarrassing,неловкий,ніяковий
zestaw,set,набор,набір
zginać,to bend,сгибать,згинати
ziarno,grain,зерно,зерно
ziemia,earth,земля,земля
zirytowany,irritated,раздражённый,роздратований
zlokalizować,to locate,локализовать,локалізувати
złoto,gold,золото,золото
złożony,complex,сложный,складний
zły,bad,плохой,поганий
zmartwiony,worried,обеспокоенный,стурбований
zmiana,change,изменение,зміна
znak,sign,знак,знак
znany,known,известный,відомий
zniżka,discount,скидка,знижка
zobowiązać,to oblige,обязать,зобов'язати
zorganizowany,organized,организованный,організований
zranić,to hurt,ранить,поранити
zraniony,injured,раненый,поранений
zrelaksowany,relaxed,расслабленный,розслаблений
zrewidować,to revise,пересмотреть,переглянути
zrezygnować,to resign,отказаться,відмовитися
związek,relationship,отношения,стосунки
zwiedzać,to sightsee,осматривать,оглядати
zwolennicy,supporters,сторонники,прихильники
zwolennik,supporter,сторонник,прихильник
zwycięstwo,victory,победа,перемога
zwyczaj,custom,обычай,звичай
życzenie,wish,пожелание,побажання
życzliwy,kind,доброжелательный,доброзичливий
zysk,profit,прибыль,прибуток
1 polish english russian ukrainian
2 absolutnie absolutely абсолютно абсолютно
3 administracja administration администрация адміністрація
4 agent agent агент агент
5 akademicki academic академический академічний
6 aktualizować to update обновлять оновлювати
7 alarm alarm сигнализация сигналізація
8 albo or или або
9 album album альбом альбом
10 alternatywny alternative альтернативный альтернативний
11 ambicja ambition амбиция амбіція
12 analiza analysis анализ аналіз
13 ani neither / nor ни ні
14 aplikacja application приложение додаток
15 asortyment assortment ассортимент асортимент
16 asystent assistant ассистент асистент
17 atak attack атака атака
18 atmosfera atmosphere атмосфера атмосфера
19 atrakcja attraction аттракцион атракціон
20 automatycznie automatically автоматически автоматично
21 automatyczny automatic автоматический автоматичний
22 autorytet authority авторитет авторитет
23 autostrada highway автострада автострада
24 bąbel bubble пузырь бульбашка
25 badać to research исследовать досліджувати
26 bałagan mess беспорядок безлад
27 bardzo very очень дуже
28 bateria battery батарея батарея
29 bawełna cotton хлопок бавовна
30 bęben drum барабан барабан
31 bezpieczeństwo safety безопасность безпека
32 bezpośrednio directly непосредственно безпосередньо
33 bezrobocie unemployment безработица безробіття
34 bezrobotny unemployed безработный безробітний
35 bieda poverty бедность бідність
36 bielizna underwear нижнее бельё нижня білизна
37 bieżący current текущий поточний
38 bitwa battle битва битва
39 biwakować to camp ходить в поход ходити в похід
40 blady pale бледный блідий
41 blisko near близко близько
42 bliźniak twin близнец близнюк
43 blokować to block блокировать блокувати
44 błoto mud грязь багнюка
45 błyszczący shiny блестящий блискучий
46 bójka fight драка бійка
47 bolesny painful болезненный болісний
48 brakować to lack не хватать бракувати
49 brzeg shore берег берег
50 brzydki ugly некрасивый негарний
51 burza storm гроза гроза
52 całkowicie completely полностью повністю
53 całkowity total полный повний
54 całość whole целое ціле
55 całować to kiss целовать цілувати
56 cel goal цель мета
57 celować to aim целиться цілитися
58 cenny valuable ценный цінний
59 centralny central центральный центральний
60 centrować to center центрировать центрувати
61 centrum center центр центр
62 ceremonia ceremony церемония церемонія
63 chociaż although хотя хоча
64 ciągły continuous непрерывный безперервний
65 ciągnięcie pulling тяга тягнення
66 ciasny tight тесный тісний
67 cichy quiet тихий тихий
68 ciecz liquid жидкость рідина
69 cierpieć to suffer страдать страждати
70 ciśnienie pressure давление тиск
71 codziennie daily ежедневно щоденно
72 cokolwiek anything что угодно що завгодно
73 cytat quote цитата цитата
74 cytować to quote цитировать цитувати
75 czapka cap шапка шапка
76 często often часто часто
77 czy whether ли чи
78 czyj whose чей чий
79 czyn deed поступок вчинок
80 dalej further дальше далі
81 daleki far далёкий далекий
82 definicja definition определение визначення
83 definiować to define определять визначати
84 definitywny definitive окончательный остаточний
85 dekada decade десятилетие десятиліття
86 diament diamond алмаз діамант
87 długość length длина довжина
88 dno bottom дно дно
89 doceniać to appreciate ценить цінувати
90 dodanie addition добавление додавання
91 dodatkowy additional дополнительный додатковий
92 dokument document документ документ
93 dołączyć to join присоединиться приєднатися
94 dom home дом дім
95 doradzać to advise советовать радити
96 doskonale perfectly отлично відмінно
97 dostarczać to deliver доставлять доставляти
98 dostarczyć to deliver доставить доставити
99 dostęp access доступ доступ
100 doświadczenie experience опыт досвід
101 doświadczony experienced опытный досвідчений
102 dotyk touch прикосновение дотик
103 do tyłu backwards назад назад
104 drugi second второй другий
105 drugorzędny secondary второстепенный другорядний
106 druk printing печать друк
107 drżenie trembling дрожь тремтіння
108 duch spirit дух дух
109 dumny proud гордый гордий
110 dużo many много багато
111 dym smoke дым дим
112 dyrektor director директор директор
113 dżentelmen gentleman джентльмен джентльмен
114 działanie action действие дія
115 dzieciństwo childhood детство дитинство
116 dzielnica district район район
117 dziennik diary дневник щоденник
118 dziwny strange странный дивний
119 dzwon bell колокол дзвін
120 dzwonić to call звонить телефонувати
121 edukacyjny educational образовательный освітній
122 edukować to educate обучать навчати
123 efektywnie effectively эффективно ефективно
124 ekonomiczny economic экономический економічний
125 ekscytacja excitement волнение хвилювання
126 ekspert expert эксперт експерт
127 eksperymentować to experiment экспериментировать експериментувати
128 eksplodować to explode взрываться вибухати
129 eksplozja explosion взрыв вибух
130 eksport export экспорт експорт
131 elegancki elegant элегантный елегантний
132 element element элемент елемент
133 emeryt retiree пенсионер пенсіонер
134 emocja emotion эмоция емоція
135 etykieta label этикетка етикетка
136 ewentualnie possibly возможно можливо
137 fabuła plot сюжет сюжет
138 fascynujący fascinating захватывающий захопливий
139 federalny federal федеральный федеральний
140 filmować to film снимать знімати
141 finansowy financial финансовый фінансовий
142 flaga flag флаг прапор
143 fokus focus фокус фокус
144 fotograf photographer фотограф фотограф
145 fotografia photography фотография фотографія
146 funkcja function функция функція
147 futro fur мех хутро
148 gałąź branch ветка гілка
149 garaż garage гараж гараж
150 gardło throat горло горло
151 garnek pot кастрюля каструля
152 gdziekolwiek anywhere где угодно де завгодно
153 gdzieś somewhere где-то десь
154 gigantyczny gigantic гигантский гігантський
155 gładki smooth гладкий гладкий
156 gleba soil почва ґрунт
157 głęboki deep глубокий глибокий
158 globalny global глобальный глобальний
159 głośny loud громкий голосний
160 głosować to vote голосовать голосувати
161 głównie mainly в основном в основному
162 głupi stupid глупый дурний
163 gorzej worse хуже гірше
164 gospodarka economy экономика економіка
165 gospodarz host хозяин господар
166 granica border граница кордон
167 groza horror ужас жах
168 gryźć to bite кусать кусати
169 gwałtowny violent резкий різкий
170 gwiazda star звезда зірка
171 gwóźdź nail гвоздь цвях
172 handlować to trade торговать торгувати
173 historyczny historical исторический історичний
174 hojny generous щедрый щедрий
175 huragan hurricane ураган ураган
176 ich their их їх
177 igła needle игла голка
178 ignorować to ignore игнорировать ігнорувати
179 imigrant immigrant иммигрант іммігрант
180 imponujący impressive впечатляющий вражаючий
181 import import импорт імпорт
182 indywidualny individual индивидуальный індивідуальний
183 inteligencja intelligence интеллект інтелект
184 intencja intention намерение намір
185 inwestować to invest инвестировать інвестувати
186 inżynieria engineering инженерия інженерія
187 irytujący irritating раздражающий дратівливий
188 iść to go идти йти
189 jądrowy nuclear ядерный ядерний
190 jakikolwiek any любой будь-який
191 jasny bright светлый світлий
192 jeden one один один
193 język language язык мова
194 kabel cable кабель кабель
195 kampania campaign кампания кампанія
196 kanał channel канал канал
197 kandydat candidate кандидат кандидат
198 kapitan captain капитан капітан
199 kara punishment наказание покарання
200 kategoria category категория категорія
201 każdy every каждый кожен
202 kiedy when когда коли
203 kiedykolwiek ever когда-либо коли-небудь
204 kilka several несколько кілька
205 kłamstwo lie ложь брехня
206 klauzula clause пункт пункт
207 klawiatura keyboard клавиатура клавіатура
208 klient client клиент клієнт
209 kliknąć to click кликнуть клацнути
210 klucz key ключ ключ
211 kobieta woman женщина жінка
212 kolekcja collection коллекция колекція
213 kolorowy colorful цветной кольоровий
214 komentować to comment комментировать коментувати
215 komercyjny commercial коммерческий комерційний
216 komunikacja communication коммуникация комунікація
217 koncentrować to concentrate концентрировать концентрувати
218 koniecznie necessarily обязательно обов'язково
219 konkretnie specifically конкретно конкретно
220 konkurencyjny competitive конкурентоспособный конкурентоспроможний
221 konkurent competitor конкурент конкурент
222 konsekwencja consequence последствие наслідок
223 konsument consumer потребитель споживач
224 kontakt contact контакт контакт
225 konto account счёт рахунок
226 kontrast contrast контраст контраст
227 kontrola control контроль контроль
228 kopalnia mine шахта шахта
229 kopia copy копия копія
230 kopnąć to kick пнуть копнути
231 korzystać to use пользоваться користуватися
232 kostium costume костюм костюм
233 krawat tie галстук краватка
234 krawędź edge край край
235 krążyć to circulate циркулировать циркулювати
236 kremowy creamy кремовый кремовий
237 krewny relative родственник родич
238 królewski royal королевский королівський
239 kropla drop капля крапля
240 kryminalny criminal криминальный кримінальний
241 krzyk scream крик крик
242 krzyż cross крест хрест
243 ksiądz priest священник священник
244 książę prince князь князь
245 księżniczka princess принцесса принцеса
246 kukurydza corn кукуруза кукурудза
247 kulturalny cultural культурный культурний
248 kurz dust пыль пил
249 kwadrat square квадрат квадрат
250 kwalifikacja qualification квалификация кваліфікація
251 kwalifikować to qualify квалифицировать кваліфікувати
252 laboratorium laboratory лаборатория лабораторія
253 lać to pour лить лити
254 łączyć to connect соединять з'єднувати
255 łagodny gentle мягкий м'який
256 łańcuch chain цепь ланцюг
257 latający flying летающий літаючий
258 leczenie treatment лечение лікування
259 ledwie barely едва ледве
260 lepszy better лучший кращий
261 liczba number число число
262 limit limit лимит ліміт
263 lina rope верёвка мотузка
264 liść leaf лист лист
265 literatura literature литература література
266 lokalizacja location местоположение місцезнаходження
267 ludzie people люди люди
268 ludzki human человеческий людський
269 luksus luxury роскошь розкіш
270 machnąć to wave махнуть махнути
271 magia magic магия магія
272 mąka flour мука борошно
273 mało little мало мало
274 mały small маленький малий
275 małżeństwo marriage брак шлюб
276 marka brand бренд бренд
277 marketing marketing маркетинг маркетинг
278 marnować to waste тратить впустую витрачати даремно
279 martwić to worry беспокоить хвилювати
280 martwienie worrying беспокойство хвилювання
281 marynarz sailor моряк моряк
282 mentalny mental ментальный ментальний
283 mężczyzna man мужчина чоловік
284 miejscowy local местный місцевий
285 mierzyć to measure измерять вимірювати
286 mięsień muscle мышца м'яз
287 mieszać to mix мешать змішувати
288 mieszanka mixture смесь суміш
289 mistrz master мастер майстер
290 młodość youth молодость молодість
291 młody young молодой молодий
292 mniej less меньше менше
293 mocno strongly сильно сильно
294 modlić to pray молиться молитися
295 modlitwa prayer молитва молитва
296 modny fashionable модный модний
297 moneta coin монета монета
298 morderstwo murder убийство вбивство
299 mówiony spoken разговорный розмовний
300 muszla shell ракушка мушля
301 nagle suddenly вдруг раптом
302 nagłówek headline заголовок заголовок
303 nagranie recording запись запис
304 najbardziej most больше всего найбільше
305 najgorszy worst худший найгірший
306 najlepszy best лучший найкращий
307 najmniej least меньше всего найменше
308 najnowszy newest новейший найновіший
309 najwyższy highest высочайший найвищий
310 namiot tent палатка намет
311 napisany written написанный написаний
312 napiwek tip чаевые чайові
313 naprawa repair ремонт ремонт
314 naprzeciw opposite напротив навпроти
315 naród nation нация нація
316 narracja narration повествование оповідь
317 narta ski лыжа лижа
318 nasionko seed семечко насіння
319 nastawienie attitude настрой налаштування
320 następny next следующий наступний
321 nastrój mood настроение настрій
322 nasz our наш наш
323 naturalnie naturally естественно природно
324 natychmiastowy immediate немедленный негайний
325 naukowy scientific научный науковий
326 niedbały negligent небрежный недбалий
327 niedogodność inconvenience неудобство незручність
328 nielegalny illegal нелегальный нелегальний
329 nienawiść hatred ненависть ненависть
330 niepotrzebny unnecessary ненужный непотрібний
331 nieprzyjemny unpleasant неприятный неприємний
332 nieśmiały shy застенчивый сором'язливий
333 niesprawiedliwy unfair несправедливый несправедливий
334 niewiarygodnie incredibly невероятно неймовірно
335 niewinny innocent невинный невинний
336 niewolnik slave раб раб
337 niewygodny uncomfortable неудобный незручний
338 niezbędny essential необходимый необхідний
339 niezdolny incapable неспособный нездатний
340 niski low низкий низький
341 normalny normal нормальный нормальний
342 obcy foreign чужой чужий
343 obecnie currently в настоящее время наразі
344 obecny present настоящий теперішній
345 obietnica promise обещание обіцянка
346 objaw symptom симптом симптом
347 obok next to рядом поруч
348 obowiązek duty обязанность обов'язок
349 ocena rating оценка оцінка
350 oceniać to rate оценивать оцінювати
351 oczekiwany expected ожидаемый очікуваний
352 oczywiście of course конечно звичайно
353 oczywisty obvious очевидный очевидний
354 od from от від
355 odcinek episode эпизод епізод
356 oddech breath дыхание дихання
357 oddychać to breathe дышать дихати
358 oddychanie breathing дыхание дихання
359 oddzielić to separate отделить відокремити
360 odjazd departure отъезд від'їзд
361 odległy distant далёкий далекий
362 odpoczynek rest отдых відпочинок
363 odpowiedni suitable подходящий відповідний
364 odpowiedź answer ответ відповідь
365 odpowiedzialność responsibility ответственность відповідальність
366 odpowiedzialny responsible ответственный відповідальний
367 odrzucać to reject отклонять відхиляти
368 odważny brave смелый сміливий
369 odzwierciedlać to reflect отражать відображати
370 oferta offer предложение пропозиція
371 ofiara victim жертва жертва
372 ofiarować to offer предлагать пропонувати
373 oficjalny official официальный офіційний
374 oglądać to watch смотреть дивитися
375 ogłosić to announce объявить оголосити
376 ogłoszenie announcement объявление оголошення
377 ogólnoświatowy worldwide всемирный всесвітній
378 ogon tail хвост хвіст
379 ogrzewanie heating отопление опалення
380 okazja opportunity возможность можливість
381 okładka cover обложка обкладинка
382 około about около близько
383 określić to define определить визначити
384 okropny terrible ужасный жахливий
385 okrutny cruel жестокий жорстокий
386 ołów lead свинец свинець
387 opieka care забота турбота
388 opłata fee плата плата
389 opona tire шина шина
390 oprócz except кроме крім
391 oprogramowanie software программное обеспечение програмне забезпечення
392 opróżnić to empty опустошить спорожнити
393 organizator organizer организатор організатор
394 oryginał original оригинал оригінал
395 osiągnięcie achievement достижение досягнення
396 osobiście personally лично особисто
397 osobno separately отдельно окремо
398 ostry sharp острый гострий
399 ostrzegać to warn предупреждать попереджати
400 ostrzeżenie warning предупреждение попередження
401 oszukać to cheat обмануть обдурити
402 oszukiwać to deceive обманывать обманювати
403 paczka package посылка посилка
404 palec finger палец палець
405 paliwo fuel топливо паливо
406 paragon receipt чек чек
407 pasja passion страсть пристрасть
408 pasować to fit подходить підходити
409 patelnia frying pan сковорода сковорода
410 pchnięcie push толчок поштовх
411 personel staff персонал персонал
412 piasek sand песок пісок
413 piec oven печь піч
414 piękno beauty красота краса
415 pierwotnie originally изначально спочатку
416 pierwotny original первоначальный первісний
417 pierwszy first первый перший
418 pijany drunk пьяный п'яний
419 pikantny spicy острый гострий
420 pisać to write писать писати
421 plama stain пятно пляма
422 planowanie planning планирование планування
423 plasterek slice ломтик скибка
424 plastikowy plastic пластиковый пластиковий
425 płatność payment оплата оплата
426 płeć gender пол стать
427 plik file файл файл
428 płot fence забор паркан
429 plus plus плюс плюс
430 płynąć to flow плыть плисти
431 pływanie swimming плавание плавання
432 po after после після
433 pobieranie downloading скачивание завантаження
434 pochować to bury похоронить поховати
435 podać to pass подать подати
436 podatek tax налог податок
437 podczas during во время під час
438 podgrzewać to heat подогревать підігрівати
439 podkreślać to emphasize подчёркивать підкреслювати
440 podlewać to water поливать поливати
441 podobieństwo similarity сходство схожість
442 podobnie similarly похоже схоже
443 podobny similar похожий схожий
444 podpisać to sign подписать підписати
445 podróż journey путешествие подорож
446 podstawa basis основа основа
447 podstawowy basic основной основний
448 podsumować to summarize подытожить підсумувати
449 podwójnie doubly вдвойне подвійно
450 podwyżka raise повышение підвищення
451 podzielić to divide разделить розділити
452 podziemny underground подземный підземний
453 podziwiać to admire восхищаться захоплюватися
454 poeta poet поэт поет
455 poezja poetry поэзия поезія
456 pojedynczy single одиночный одиничний
457 pojemnik container контейнер контейнер
458 pokazać to show показать показати
459 pokierować to direct направить спрямувати
460 pokolenie generation поколение покоління
461 pokryty covered покрытый покритий
462 połączenie connection соединение з'єднання
463 połączyć to connect соединить з'єднати
464 polityczny political политический політичний
465 polityk politician политик політик
466 polityka politics политика політика
467 półka shelf полка полиця
468 północ north север північ
469 północny northern северный північний
470 połowa half половина половина
471 polować to hunt охотиться полювати
472 położony located расположенный розташований
473 położyć to put положить покласти
474 południe south юг південь
475 południowy southern южный південний
476 pomimo despite несмотря на незважаючи на
477 pomnik monument памятник пам'ятник
478 pomóc to help помочь допомогти
479 pop pop поп поп
480 poprawa improvement улучшение покращення
481 poprawnie correctly правильно правильно
482 poprzedni previous предыдущий попередній
483 porównanie comparison сравнение порівняння
484 port port порт порт
485 portret portrait портрет портрет
486 porządkowy ordinal порядковый порядковий
487 pośpiech hurry спешка поспіх
488 pośredni indirect косвенный непрямий
489 poszukiwanie search поиск пошук
490 potężny powerful могущественный могутній
491 potrzebować to need нуждаться потребувати
492 potwierdzić to confirm подтвердить підтвердити
493 poważnie seriously серьёзно серйозно
494 powiązany related связанный пов'язаний
495 powierzchnia surface поверхность поверхня
496 powiesić to hang повесить повісити
497 powinien should должен повинен
498 powtarzany repeated повторяемый повторюваний
499 powtórzenie repetition повторение повторення
500 poziomy horizontal горизонтальный горизонтальний
501 pozostać to remain остаться залишитися
502 praktyczny practical практичный практичний
503 prasa press пресса преса
504 prawda truth правда правда
505 prawidłowo correctly правильно правильно
506 prawny legal правовой правовий
507 prezentacja presentation презентация презентація
508 prezentować to present представлять представляти
509 próba attempt попытка спроба
510 próbka sample образец зразок
511 problem problem проблема проблема
512 procent percent процент відсоток
513 producent producer производитель виробник
514 produkcja production производство виробництво
515 prognoza forecast прогноз прогноз
516 programować to program программировать програмувати
517 promować to promote продвигать просувати
518 prosić to ask просить просити
519 prosty simple простой простий
520 proszek powder порошок порошок
521 protest protest протест протест
522 prywatny private частный приватний
523 przechowywać to store хранить зберігати
524 przed before перед перед
525 przedstawienie performance представление вистава
526 przekonać to convince убедить переконати
527 przełączyć to switch переключить перемкнути
528 przeprosić to apologize извиниться вибачитися
529 przerażający terrifying ужасающий жахливий
530 przestraszony scared испуганный наляканий
531 przestraszyć to scare испугать налякати
532 przeszkadzać to disturb мешать заважати
533 przeszły past прошлый минулий
534 przetłumaczyć to translate перевести перекласти
535 przewodnik guide гид гід
536 przez through через через
537 przeżyć to survive пережить пережити
538 przybycie arrival прибытие прибуття
539 przyciągać to attract привлекать приваблювати
540 przygotowany prepared подготовленный підготовлений
541 przyjaźń friendship дружба дружба
542 przyjemność pleasure удовольствие задоволення
543 przyjemny pleasant приятный приємний
544 przykleić to stick приклеить приклеїти
545 przypominać to remind напоминать нагадувати
546 przysługa favor услуга послуга
547 przyznać to admit признать визнати
548 pszczoła bee пчела бджола
549 publiczny public публичный публічний
550 pukanie knocking стук стук
551 pusty empty пустой порожній
552 ramka frame рамка рамка
553 ranny wounded раненый поранений
554 rasa breed порода порода
555 reakcja reaction реакция реакція
556 recenzja review обзор огляд
557 redaktor editor редактор редактор
558 referencja reference референция референція
559 regularnie regularly регулярно регулярно
560 rękawiczka glove перчатка рукавичка
561 relacja relation отношение відношення
562 relaksujący relaxing расслабляющий розслаблюючий
563 religia religion религия релігія
564 religijny religious религиозный релігійний
565 reprezentować to represent представлять представляти
566 rezerwacja reservation бронирование бронювання
567 robot robot робот робот
568 rodzimy native родной рідний
569 romantyczny romantic романтический романтичний
570 również also также також
571 równowaga balance равновесие рівновага
572 równy equal равный рівний
573 rozcięcie cut разрез розріз
574 rozczarowany disappointed разочарованный розчарований
575 rozczarowujący disappointing разочаровывающий розчаровуючий
576 rozdział chapter глава розділ
577 rozmowa conversation разговор розмова
578 różny different разный різний
579 rozprzestrzeniać to spread распространять поширювати
580 rozrywka entertainment развлечение розвага
581 rozsądny reasonable разумный розумний
582 rozszerzać to expand расширять розширювати
583 rozwój development развитие розвиток
584 ruch movement движение рух
585 rura pipe труба труба
586 ryzyko risk риск ризик
587 rząd government правительство уряд
588 rzadki rare редкий рідкісний
589 rzadko rarely редко рідко
590 rządzić to rule править правити
591 rzeczy things вещи речі
592 rzeczywiście indeed действительно дійсно
593 rzeczywistość reality реальность реальність
594 rzeźba sculpture скульптура скульптура
595 sąd court суд суд
596 sam alone один сам
597 samotny lonely одинокий самотній
598 scenariusz script сценарий сценарій
599 schłodzić to cool охладить охолодити
600 ścieżka path тропинка стежка
601 sekret secret секрет секрет
602 sen dream сон сон
603 serce heart сердце серце
604 sieć network сеть мережа
605 siła strength сила сила
606 skała rock скала скеля
607 skanować to scan сканировать сканувати
608 skarga complaint жалоба скарга
609 składać to compose составлять складати
610 skoczyć to jump прыгнуть стрибнути
611 skok jump прыжок стрибок
612 skóra skin кожа шкіра
613 skrzydło wing крыло крило
614 skuteczny effective эффективный ефективний
615 słodycz sweetness сладость солодкість
616 sługa servant слуга слуга
617 smakować to taste пробовать куштувати
618 smażyć to fry жарить смажити
619 śmigłowiec helicopter вертолёт гелікоптер
620 sortować to sort сортировать сортувати
621 spokój calm спокойствие спокій
622 spokojny calm спокойный спокійний
623 spotkanie meeting встреча зустріч
624 spowodować to cause вызвать спричинити
625 spowolnić to slow down замедлить уповільнити
626 spożywać to consume потреблять споживати
627 sprawiedliwie fairly справедливо справедливо
628 sprytny clever хитрый хитрий
629 srebrny silver серебряный срібний
630 średni medium средний середній
631 średnia average среднее середнє
632 środek middle середина середина
633 środowiskowy environmental экологический екологічний
634 stadion stadium стадион стадіон
635 stały constant постоянный постійний
636 standard standard стандарт стандарт
637 staromodny old-fashioned старомодный старомодний
638 statystyka statistics статистика статистика
639 strata loss потеря втрата
640 strażnik guard охранник охоронець
641 streszczenie summary резюме резюме
642 strzelać to shoot стрелять стріляти
643 studio studio студия студія
644 stwierdzić to state заявить заявити
645 substancja substance вещество речовина
646 sufit ceiling потолок стеля
647 suszyć to dry сушить сушити
648 świadomy aware осознанный свідомий
649 świecić to shine светить світити
650 święto holiday праздник свято
651 sygnalizować to signal сигнализировать сигналізувати
652 szacunek respect уважение повага
653 szafka cabinet шкафчик шафка
654 szalony crazy сумасшедший божевільний
655 szczególnie especially особенно особливо
656 szczęście happiness счастье щастя
657 szczotkować to brush чистить щёткой чистити щіткою
658 szkoda pity жаль шкода
659 sznurek string верёвка мотузка
660 szorstki rough шероховатый шорсткий
661 szpilka pin булавка шпилька
662 tajemnica secret тайна таємниця
663 tak yes да так
664 taki such такой такий
665 talent talent талант талант
666 tani cheap дешёвый дешевий
667 taśma tape лента стрічка
668 techniczny technical технический технічний
669 technika technique техника техніка
670 temat topic тема тема
671 teoria theory теория теорія
672 teraz now сейчас зараз
673 tkanina fabric ткань тканина
674 tłumaczenie translation перевод переклад
675 to this это це
676 toczyć to roll катить котити
677 tona ton тонна тонна
678 tonąć to drown тонуть тонути
679 tożsamość identity идентичность ідентичність
680 transportować to transport транспортировать транспортувати
681 trend trend тренд тренд
682 trener coach тренер тренер
683 trochę little немного трохи
684 troszczyć to care заботиться піклуватися
685 trucizna poison яд отрута
686 trudność difficulty трудность трудність
687 trujący poisonous ядовитый отруйний
688 trwać to last длиться тривати
689 twierdzić to claim утверждать стверджувати
690 typowo typically типично типово
691 ubrany dressed одетый одягнений
692 uciec to escape убежать втекти
693 uczciwy honest честный чесний
694 uczestniczyć to participate участвовать брати участь
695 udawać to pretend притворяться прикидатися
696 udowodnić to prove доказать довести
697 udział participation участие участь
698 ukarać to punish наказать покарати
699 ukończyć to complete закончить закінчити
700 umieścić to place разместить розмістити
701 umowa contract договор договір
702 uprzątnąć to clean up убрать прибрати
703 uśmiech smile улыбка усмішка
704 ustawienie setting настройка налаштування
705 utalentowany talented талантливый талановитий
706 uwolnić to release освободить звільнити
707 używany used подержанный вживаний
708 walka fight борьба боротьба
709 waluta currency валюта валюта
710 warga lip губа губа
711 warstwa layer слой шар
712 wartość value ценность цінність
713 wartościowy valuable ценный цінний
714 wątpliwość doubt сомнение сумнів
715 ważność importance важность важливість
716 ważyć to weigh взвешивать зважувати
717 wcześniej earlier раньше раніше
718 wdzięczny grateful благодарный вдячний
719 węgiel coal уголь вугілля
720 wejście entrance вход вхід
721 wełna wool шерсть вовна
722 wersja version версия версія
723 wesoły cheerful весёлый веселий
724 wewnątrz inside внутри всередині
725 wewnętrzny internal внутренний внутрішній
726 wiara faith вера віра
727 wiarygodny credible достоверный достовірний
728 wiązać to tie связывать зв'язувати
729 widok view вид краєвид
730 widz viewer зритель глядач
731 więcej more больше більше
732 wiersz poem стихотворение вірш
733 wieś village деревня село
734 więzień prisoner заключённый в'язень
735 winny guilty виновный винний
736 wiodący leading ведущий провідний
737 własność property собственность власність
738 własny own собственный власний
739 wokół around вокруг навколо
740 wola will воля воля
741 wolontariusz volunteer волонтёр волонтер
742 wpływ influence влияние вплив
743 wrażenie impression впечатление враження
744 wróg enemy враг ворог
745 wschód east восток схід
746 wschodni eastern восточный східний
747 wsiąść to get on сесть сісти
748 wskazać to indicate указать вказати
749 wskazówka hint подсказка підказка
750 wsparcie support поддержка підтримка
751 wspomnieć to mention упомянуть згадати
752 wszyscy everyone все усі
753 wybory election выборы вибори
754 wydatki expenses расходы витрати
755 wygodny comfortable удобный зручний
756 wyimaginowany imaginary воображаемый уявний
757 wyjście exit выход вихід
758 wykład lecture лекция лекція
759 wykształcony educated образованный освічений
760 wykwalifikowany qualified квалифицированный кваліфікований
761 wymagać to require требовать вимагати
762 wymiana exchange обмен обмін
763 wynająć to rent арендовать орендувати
764 wynik result результат результат
765 wyróżnić to distinguish выделить виділити
766 wyścigi racing гонки перегони
767 wysiłek effort усилие зусилля
768 wysłać to send отправить надіслати
769 wystąpić to appear выступить виступити
770 wystarczająco enough достаточно достатньо
771 wystawa exhibition выставка виставка
772 wyzwanie challenge вызов виклик
773 wzdłuż along вдоль уздовж
774 wzrost growth рост зріст
775 zaangażowany engaged вовлечённый залучений
776 zaawansowany advanced продвинутый просунутий
777 zabawiać to entertain развлекать розважати
778 zabawny funny смешной смішний
779 zabójstwo murder убийство вбивство
780 zachęcać to encourage поощрять заохочувати
781 zachód west запад захід
782 zachodni western западный західний
783 zadanie task задание завдання
784 zadowolony satisfied довольный задоволений
785 zafundować to treat угостить пригостити
786 żagiel sail парус вітрило
787 zakazać to forbid запретить заборонити
788 zakończenie ending окончание закінчення
789 zakończyć to finish закончить закінчити
790 zalewać to flood заливать заливати
791 zaliczenie credit зачёт залік
792 zamarznięty frozen замёрзший замерзлий
793 zamek castle замок замок
794 zamierzać to intend намереваться мати намір
795 zamknięty closed закрытый закритий
796 zamrozić to freeze заморозить заморозити
797 zapach smell запах запах
798 zapał enthusiasm энтузиазм ентузіазм
799 zapiąć to fasten застегнуть застебнути
800 żart joke шутка жарт
801 zarządzanie management управление управління
802 zaskoczyć to surprise удивить здивувати
803 zasłona curtain занавеска завіса
804 zasób resource ресурс ресурс
805 zatrudnić to hire нанять найняти
806 zatrudnienie employment трудоустройство працевлаштування
807 zauważyć to notice заметить помітити
808 zawartość content содержимое вміст
809 zawód profession профессия професія
810 zawstydzony embarrassed смущённый збентежений
811 zazwyczaj usually обычно зазвичай
812 zbierać to collect собирать збирати
813 zdecydowany determined решительный рішучий
814 zdezorientować to disorient дезориентировать дезорієнтувати
815 zdezorientowany confused растерянный розгублений
816 zdumiony amazed изумлённый здивований
817 żelazo iron железо залізо
818 żenujący embarrassing неловкий ніяковий
819 zestaw set набор набір
820 zginać to bend сгибать згинати
821 ziarno grain зерно зерно
822 ziemia earth земля земля
823 zirytowany irritated раздражённый роздратований
824 zlokalizować to locate локализовать локалізувати
825 złoto gold золото золото
826 złożony complex сложный складний
827 zły bad плохой поганий
828 zmartwiony worried обеспокоенный стурбований
829 zmiana change изменение зміна
830 znak sign знак знак
831 znany known известный відомий
832 zniżka discount скидка знижка
833 zobowiązać to oblige обязать зобов'язати
834 zorganizowany organized организованный організований
835 zranić to hurt ранить поранити
836 zraniony injured раненый поранений
837 zrelaksowany relaxed расслабленный розслаблений
838 zrewidować to revise пересмотреть переглянути
839 zrezygnować to resign отказаться відмовитися
840 związek relationship отношения стосунки
841 zwiedzać to sightsee осматривать оглядати
842 zwolennicy supporters сторонники прихильники
843 zwolennik supporter сторонник прихильник
844 zwycięstwo victory победа перемога
845 zwyczaj custom обычай звичай
846 życzenie wish пожелание побажання
847 życzliwy kind доброжелательный доброзичливий
848 zysk profit прибыль прибуток