LearnSpace: full-stack educational whiteboard platform
Node.js/Express backend + vanilla JS frontend. Features: real-time collaborative whiteboard (SSE), multi-page support, LaTeX formulas, shapes/connectors, coordinate systems, number lines, compass, zoom/pan, Catmull-Rom pencil smoothing, ruler/protractor with rotation & resize controls, minimap navigation overlay, auto-measurements, multi-page thumbnails sidebar, PNG export, page templates. Student/teacher workflows: classes, assignments, library, dashboard. Mobile responsive. SQLite (better-sqlite3). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
const { DatabaseSync } = require('node:sqlite');
|
||||
const path = require('path');
|
||||
|
||||
const fs = require('fs');
|
||||
// Resolve DB_PATH: if absolute — use as-is; if relative — resolve from backend/ dir;
|
||||
// fallback to __dirname-relative default so CWD never matters.
|
||||
const _rawPath = process.env.DB_PATH;
|
||||
const _backendDir = path.join(__dirname, '../../');
|
||||
const dbPath = _rawPath
|
||||
? (path.isAbsolute(_rawPath) ? _rawPath : path.resolve(_backendDir, _rawPath))
|
||||
: path.join(__dirname, '../../data/learnspace.db');
|
||||
const dbDir = path.dirname(dbPath);
|
||||
if (!fs.existsSync(dbDir)) fs.mkdirSync(dbDir, { recursive: true });
|
||||
|
||||
// Auto-migrate from old location (backend/learnspace.db → backend/data/learnspace.db)
|
||||
const oldPath = path.join(__dirname, '../../learnspace.db');
|
||||
if (!fs.existsSync(dbPath) && fs.existsSync(oldPath)) {
|
||||
fs.copyFileSync(oldPath, dbPath);
|
||||
// Also copy WAL/SHM if present
|
||||
if (fs.existsSync(oldPath + '-wal')) fs.copyFileSync(oldPath + '-wal', dbPath + '-wal');
|
||||
if (fs.existsSync(oldPath + '-shm')) fs.copyFileSync(oldPath + '-shm', dbPath + '-shm');
|
||||
console.log('[db] Migrated database from', oldPath, '→', dbPath);
|
||||
}
|
||||
|
||||
const db = new DatabaseSync(dbPath);
|
||||
|
||||
db.exec('PRAGMA journal_mode = WAL');
|
||||
db.exec('PRAGMA foreign_keys = ON');
|
||||
|
||||
/**
|
||||
* Run a synchronous function inside a BEGIN/COMMIT/ROLLBACK transaction.
|
||||
* Returns the value returned by fn(), or rethrows on error.
|
||||
*/
|
||||
db.transaction = function transaction(fn) {
|
||||
return (...args) => {
|
||||
db.exec('BEGIN');
|
||||
try {
|
||||
const result = fn(...args);
|
||||
db.exec('COMMIT');
|
||||
return result;
|
||||
} catch (err) {
|
||||
db.exec('ROLLBACK');
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = db;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,102 @@
|
||||
-- =============================================
|
||||
-- LearnSpace — Initial schema
|
||||
-- =============================================
|
||||
|
||||
-- Расширения
|
||||
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
||||
|
||||
-- ── Пользователи ──────────────────────────────
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
password_hash VARCHAR(255) NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
role VARCHAR(20) NOT NULL DEFAULT 'student'
|
||||
CHECK (role IN ('student', 'teacher', 'admin')),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
last_login TIMESTAMPTZ
|
||||
);
|
||||
|
||||
-- ── Предметы ──────────────────────────────────
|
||||
CREATE TABLE IF NOT EXISTS subjects (
|
||||
id SERIAL PRIMARY KEY,
|
||||
slug VARCHAR(50) UNIQUE NOT NULL,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
icon VARCHAR(10)
|
||||
);
|
||||
|
||||
INSERT INTO subjects (slug, name, icon) VALUES
|
||||
('bio', 'Биология', 'dna'),
|
||||
('chem', 'Химия', 'atom'),
|
||||
('math', 'Математика', 'compass'),
|
||||
('phys', 'Физика', 'zap')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- ── Темы ──────────────────────────────────────
|
||||
CREATE TABLE IF NOT EXISTS topics (
|
||||
id SERIAL PRIMARY KEY,
|
||||
subject_id INT NOT NULL REFERENCES subjects(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
order_index INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- ── Банк вопросов ─────────────────────────────
|
||||
CREATE TABLE IF NOT EXISTS questions (
|
||||
id SERIAL PRIMARY KEY,
|
||||
subject_id INT NOT NULL REFERENCES subjects(id) ON DELETE CASCADE,
|
||||
topic_id INT REFERENCES topics(id) ON DELETE SET NULL,
|
||||
text TEXT NOT NULL,
|
||||
difficulty SMALLINT NOT NULL DEFAULT 1 CHECK (difficulty BETWEEN 1 AND 3),
|
||||
year SMALLINT,
|
||||
explanation TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ── Варианты ответов ──────────────────────────
|
||||
CREATE TABLE IF NOT EXISTS options (
|
||||
id SERIAL PRIMARY KEY,
|
||||
question_id INT NOT NULL REFERENCES questions(id) ON DELETE CASCADE,
|
||||
text TEXT NOT NULL,
|
||||
is_correct BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
order_index SMALLINT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- ── Сессии тестирования ───────────────────────
|
||||
CREATE TABLE IF NOT EXISTS test_sessions (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
subject_id INT REFERENCES subjects(id) ON DELETE SET NULL,
|
||||
mode VARCHAR(20) NOT NULL DEFAULT 'exam'
|
||||
CHECK (mode IN ('exam', 'practice', 'topic', 'random')),
|
||||
total INT NOT NULL,
|
||||
score INT,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'in_progress'
|
||||
CHECK (status IN ('in_progress', 'completed', 'abandoned')),
|
||||
started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
finished_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
-- ── Вопросы сессии ────────────────────────────
|
||||
CREATE TABLE IF NOT EXISTS session_questions (
|
||||
id SERIAL PRIMARY KEY,
|
||||
session_id INT NOT NULL REFERENCES test_sessions(id) ON DELETE CASCADE,
|
||||
question_id INT NOT NULL REFERENCES questions(id),
|
||||
order_index INT NOT NULL
|
||||
);
|
||||
|
||||
-- ── Ответы пользователя ───────────────────────
|
||||
CREATE TABLE IF NOT EXISTS user_answers (
|
||||
id SERIAL PRIMARY KEY,
|
||||
session_id INT NOT NULL REFERENCES test_sessions(id) ON DELETE CASCADE,
|
||||
question_id INT NOT NULL REFERENCES questions(id),
|
||||
chosen_option_id INT REFERENCES options(id),
|
||||
is_correct BOOLEAN,
|
||||
time_spent_sec SMALLINT,
|
||||
answered_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ── Индексы ───────────────────────────────────
|
||||
CREATE INDEX IF NOT EXISTS idx_questions_subject ON questions(subject_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_questions_topic ON questions(topic_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_sessions_user ON test_sessions(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_answers_session ON user_answers(session_id);
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Уникальный индекс для upsert ответов
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_answers_session_question
|
||||
ON user_answers (session_id, question_id);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,667 @@
|
||||
/**
|
||||
* Seed: Математика — вопросы для тестов
|
||||
*
|
||||
* Источники:
|
||||
* — 21 вопрос ЦТ/ЦЭ (общие)
|
||||
* — 32 вопроса ЦТ 2021, Вариант 1 (A1–A18 + B1–B14)
|
||||
*
|
||||
* Запуск: node src/db/seed-math.js (из папки backend/)
|
||||
*/
|
||||
const db = require('./db');
|
||||
|
||||
/* ── helpers ─────────────────────────────────────────────────────────── */
|
||||
function getOrCreateTopic(subjectId, name) {
|
||||
const ex = db.prepare('SELECT id FROM topics WHERE subject_id=? AND LOWER(name)=LOWER(?)').get(subjectId, name);
|
||||
if (ex) return ex.id;
|
||||
return db.prepare('INSERT INTO topics (subject_id, name) VALUES (?,?)').run(subjectId, name).lastInsertRowid;
|
||||
}
|
||||
|
||||
/* ── subject ─────────────────────────────────────────────────────────── */
|
||||
const math = db.prepare("SELECT id FROM subjects WHERE slug='math'").get();
|
||||
if (!math) { console.error('Subject "math" not found. Run migrate first.'); process.exit(1); }
|
||||
const SID = math.id;
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════════
|
||||
Вопросы
|
||||
Поля: topic, difficulty, text, options[{text,correct}], explanation
|
||||
Необязательные: year (число), type ('single'|'multiple')
|
||||
═══════════════════════════════════════════════════════════════════════ */
|
||||
const questions = [
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════
|
||||
РАЗДЕЛ 1 — Общие задания ЦТ/ЦЭ
|
||||
══════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
{
|
||||
topic: 'Арифметика и степени', difficulty: 1,
|
||||
text: 'Вычислите: \\((-3)^2 - 2^3 + 1\\)',
|
||||
options: [
|
||||
{ text: '\\(2\\)', correct: true },
|
||||
{ text: '\\(4\\)', correct: false },
|
||||
{ text: '\\(0\\)', correct: false },
|
||||
{ text: '\\(-2\\)', correct: false },
|
||||
{ text: '\\(18\\)', correct: false },
|
||||
],
|
||||
explanation: '\\((-3)^2=9,\\;2^3=8.\\;9-8+1=2\\)',
|
||||
},
|
||||
{
|
||||
topic: 'Словесные задачи', difficulty: 1,
|
||||
text: 'Толя купил 3 альбома и 5 карандашей. Стоимость альбома — 1 р. 20 к., карандаша — 25 к. Сколько копеек осталось у Толи после покупки, если всего у него было 6 р.?',
|
||||
options: [
|
||||
{ text: '115 к.', correct: true },
|
||||
{ text: '145 к.', correct: false },
|
||||
{ text: '110 к.', correct: false },
|
||||
{ text: '125 к.', correct: false },
|
||||
{ text: '275 к.', correct: false },
|
||||
],
|
||||
explanation: '3·120+5·25=360+125=485 к. Остаток: 600−485=115 к.',
|
||||
},
|
||||
{
|
||||
topic: 'Теория чисел', difficulty: 1,
|
||||
text: 'Укажите формулу для нахождения делимого \\(n\\), если делитель равен 15, неполное частное \\(k\\), остаток 7:',
|
||||
options: [
|
||||
{ text: '\\(n=15k+7\\)', correct: true },
|
||||
{ text: '\\(n=15(k+7)\\)', correct: false },
|
||||
{ text: '\\(n=k+22\\)', correct: false },
|
||||
{ text: '\\(n=7k+15\\)', correct: false },
|
||||
],
|
||||
explanation: 'По теореме о делении с остатком: \\(n=d\\cdot q+r=15k+7\\)',
|
||||
},
|
||||
{
|
||||
topic: 'Тригонометрия', difficulty: 1,
|
||||
text: 'Среди значений \\(-\\dfrac{\\pi}{6};\\;\\dfrac{\\pi}{4};\\;\\dfrac{\\pi}{3};\\;-\\dfrac{3\\pi}{2};\\;-6\\pi\\) укажите то, при котором \\(\\sin x=0\\):',
|
||||
options: [
|
||||
{ text: '\\(-6\\pi\\)', correct: true },
|
||||
{ text: '\\(-\\dfrac{\\pi}{6}\\)', correct: false },
|
||||
{ text: '\\(\\dfrac{\\pi}{4}\\)', correct: false },
|
||||
{ text: '\\(-\\dfrac{3\\pi}{2}\\)', correct: false },
|
||||
],
|
||||
explanation: '\\(\\sin(-6\\pi)=0\\), так как \\(-6\\pi\\) кратно \\(\\pi\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Квадратные уравнения', difficulty: 2,
|
||||
text: 'Укажите квадратное уравнение, произведение действительных корней которого равно 5:',
|
||||
options: [
|
||||
{ text: '\\(x^2-6x+5=0\\)', correct: true },
|
||||
{ text: '\\(x^2-4x+5=0\\)', correct: false },
|
||||
{ text: '\\(x^2-5x+6=0\\)', correct: false },
|
||||
{ text: '\\(x^2+5x=0\\)', correct: false },
|
||||
],
|
||||
explanation: 'По формуле Виета произведение корней \\(=c/a\\). Для \\(x^2-6x+5=0\\): \\(5/1=5\\). Корни: \\(x=1,\\;x=5\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Тригонометрия', difficulty: 2,
|
||||
text: 'Найдите значение выражения \\(\\dfrac{38}{\\pi}\\cdot\\arcsin(-1)-|-7|\\)',
|
||||
options: [
|
||||
{ text: '\\(-26\\)', correct: true },
|
||||
{ text: '\\(-16\\)', correct: false },
|
||||
{ text: '\\(-12\\)', correct: false },
|
||||
{ text: '\\(26\\)', correct: false },
|
||||
],
|
||||
explanation: '\\(\\arcsin(-1)=-\\dfrac{\\pi}{2}\\). Тогда: \\(\\dfrac{38}{\\pi}\\cdot(-\\dfrac{\\pi}{2})-7=-19-7=-26\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Квадратные уравнения', difficulty: 1,
|
||||
text: 'Найдите нули функции \\(f(x)=x^2+4x-5\\):',
|
||||
options: [
|
||||
{ text: '\\(x=-5\\) и \\(x=1\\)', correct: true },
|
||||
{ text: '\\(x=5\\) и \\(x=-1\\)', correct: false },
|
||||
{ text: '\\(x=5\\) и \\(x=1\\)', correct: false },
|
||||
{ text: '\\(x=-5\\) и \\(x=-1\\)', correct: false },
|
||||
],
|
||||
explanation: 'Дискриминант: \\(16+20=36\\). \\(x_{1,2}=\\dfrac{-4\\pm6}{2}\\). \\(x_1=-5,\\;x_2=1\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Тригонометрия', difficulty: 2,
|
||||
text: 'Найдите \\(\\operatorname{ctg}^2\\alpha\\), если \\(\\sin\\alpha=\\dfrac{1}{5}\\):',
|
||||
options: [
|
||||
{ text: '\\(24\\)', correct: true },
|
||||
{ text: '\\(\\frac{1}{24}\\)', correct: false },
|
||||
{ text: '\\(4\\)', correct: false },
|
||||
{ text: '\\(25\\)', correct: false },
|
||||
],
|
||||
explanation: '\\(\\cos^2\\alpha=1-\\tfrac{1}{25}=\\tfrac{24}{25}\\). \\(\\operatorname{ctg}^2\\alpha=\\dfrac{24/25}{1/25}=24\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Прогрессии', difficulty: 2,
|
||||
text: 'Пятый член геометрической прогрессии равен 48, шестой — 96. Найдите первый член:',
|
||||
options: [
|
||||
{ text: '\\(3\\)', correct: true },
|
||||
{ text: '\\(1\\)', correct: false },
|
||||
{ text: '\\(2\\)', correct: false },
|
||||
{ text: '\\(4\\)', correct: false },
|
||||
],
|
||||
explanation: '\\(q=96/48=2\\). \\(b_1=48/2^4=3\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Неравенства', difficulty: 2,
|
||||
text: 'Сколько целых решений имеет неравенство \\(-3\\le2-\\dfrac{3x-2}{2}<27\\,\\)?',
|
||||
options: [
|
||||
{ text: '\\(20\\)', correct: true },
|
||||
{ text: '\\(18\\)', correct: false },
|
||||
{ text: '\\(19\\)', correct: false },
|
||||
{ text: '\\(21\\)', correct: false },
|
||||
],
|
||||
explanation: 'Решение: \\(-15\\le x\\le4\\). Целые: от −15 до 4 → \\(4-(-15)+1=20\\) чисел.',
|
||||
},
|
||||
{
|
||||
topic: 'Квадратные уравнения', difficulty: 1,
|
||||
text: 'Функция \\(f(x)=x^2+4x-5\\). Найдите сумму нулей функции.',
|
||||
options: [
|
||||
{ text: '\\(-4\\)', correct: true },
|
||||
{ text: '\\(4\\)', correct: false },
|
||||
{ text: '\\(-5\\)', correct: false },
|
||||
{ text: '\\(0\\)', correct: false },
|
||||
],
|
||||
explanation: 'По теореме Виета: \\(-b/a=-4\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Прогрессии', difficulty: 2,
|
||||
text: 'Найдите сумму всех натуральных чисел, кратных 9, которые больше 141, но меньше 170.',
|
||||
options: [
|
||||
{ text: '\\(459\\)', correct: true },
|
||||
{ text: '\\(468\\)', correct: false },
|
||||
{ text: '\\(315\\)', correct: false },
|
||||
{ text: '\\(414\\)', correct: false },
|
||||
],
|
||||
explanation: 'Числа: 144, 153, 162. Сумма: \\(144+153+162=459\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Геометрия', difficulty: 2,
|
||||
text: 'Радиус описанной окружности прямоугольного треугольника \\(ABC\\) (\\(\\angle ABC=90°\\)) равен \\(18\\sqrt{2}\\). Найдите \\(90\\cdot\\cos\\angle ACB\\), если \\(BC=6\\sqrt{2}\\).',
|
||||
options: [
|
||||
{ text: '\\(15\\)', correct: true },
|
||||
{ text: '\\(30\\)', correct: false },
|
||||
{ text: '\\(45\\)', correct: false },
|
||||
{ text: '\\(60\\)', correct: false },
|
||||
],
|
||||
explanation: '\\(AC=2R=36\\sqrt{2}\\). \\(\\cos\\angle ACB=\\dfrac{6\\sqrt{2}}{36\\sqrt{2}}=\\dfrac{1}{6}\\). Ответ: \\(15\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Словесные задачи', difficulty: 2,
|
||||
text: 'Проездной на месяц стоит 39 р., разовый — 80 к. 75% потраченной Машей суммы равны стоимости проездного. Сколько поездок она совершила?',
|
||||
options: [
|
||||
{ text: '65', correct: true },
|
||||
{ text: '52', correct: false },
|
||||
{ text: '75', correct: false },
|
||||
{ text: '60', correct: false },
|
||||
],
|
||||
explanation: '75% суммы=39 р. → сумма=52 р.=5200 к. Поездок: \\(5200/80=65\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Неравенства', difficulty: 2,
|
||||
text: 'Найдите сумму наименьшего и наибольшего целых решений неравенства \\(-3\\le2-\\dfrac{3x-2}{2}<27\\).',
|
||||
options: [
|
||||
{ text: '\\(-11\\)', correct: true },
|
||||
{ text: '\\(-10\\)', correct: false },
|
||||
{ text: '\\(-12\\)', correct: false },
|
||||
{ text: '\\(-14\\)', correct: false },
|
||||
],
|
||||
explanation: 'Целые решения: от −15 до 4. Сумма крайних: \\(-15+4=-11\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Функции', difficulty: 2,
|
||||
text: 'Функция \\(y=f(x)\\) чётная. Точки \\(A(3;-\\tfrac{2}{3})\\) и \\(B(6;-\\tfrac{3}{4})\\) на графике. Найдите \\(6f(-3)+8f(-6)\\).',
|
||||
options: [
|
||||
{ text: '\\(-10\\)', correct: true },
|
||||
{ text: '\\(10\\)', correct: false },
|
||||
{ text: '\\(-6\\)', correct: false },
|
||||
{ text: '\\(-4\\)', correct: false },
|
||||
],
|
||||
explanation: '\\(f(-3)=f(3)=-\\tfrac{2}{3}\\), \\(f(-6)=-\\tfrac{3}{4}\\). \\(6\\cdot(-\\tfrac{2}{3})+8\\cdot(-\\tfrac{3}{4})=-4-6=-10\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Логарифмы', difficulty: 3,
|
||||
text: 'Найдите произведение корней уравнения \\(\\log_2^2x-2\\log_2x=\\log_224-\\log_23\\), умноженное на 11.',
|
||||
options: [
|
||||
{ text: '\\(44\\)', correct: true },
|
||||
{ text: '\\(22\\)', correct: false },
|
||||
{ text: '\\(88\\)', correct: false },
|
||||
{ text: '\\(48\\)', correct: false },
|
||||
],
|
||||
explanation: 'Пусть \\(t=\\log_2x\\): \\(t^2-2t=3\\Rightarrow t=3\\) или \\(t=-1\\). Корни: \\(x=8,\\;x=\\tfrac{1}{2}\\). Произведение \\(=4\\). \\(4\\times11=44\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Геометрия', difficulty: 3,
|
||||
text: 'Плоскость, параллельная основанию треугольной пирамиды, делит высоту в отношении \\(5:3\\) от вершины. Найдите площадь сечения, если она меньше площади основания на 39.',
|
||||
options: [
|
||||
{ text: '\\(25\\)', correct: true },
|
||||
{ text: '\\(36\\)', correct: false },
|
||||
{ text: '\\(16\\)', correct: false },
|
||||
{ text: '\\(49\\)', correct: false },
|
||||
],
|
||||
explanation: '\\(k=\\tfrac{5}{8}\\). \\(S_{\\text{осн}}-\\tfrac{25}{64}S_{\\text{осн}}=39\\Rightarrow S_{\\text{осн}}=64\\). \\(S_{\\text{сеч}}=25\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Показательные неравенства', difficulty: 3,
|
||||
text: 'Найдите наименьшее целое решение неравенства \\(8^{2x-32}+10\\cdot4^{3x-49}>56\\).',
|
||||
options: [
|
||||
{ text: '\\(17\\)', correct: true },
|
||||
{ text: '\\(16\\)', correct: false },
|
||||
{ text: '\\(18\\)', correct: false },
|
||||
{ text: '\\(15\\)', correct: false },
|
||||
],
|
||||
explanation: 'Пусть \\(t=2^{6x-98}\\): \\(4t+10t>56\\Rightarrow t>4\\Rightarrow x>16{,}\\overline{6}\\). Мин. целое: 17.',
|
||||
},
|
||||
{
|
||||
topic: 'Логарифмы', difficulty: 3,
|
||||
text: 'Найдите произведение наименьшего и наибольшего целых решений неравенства \\(\\log_3^2(x+12)-\\log_3(x+12)-6<0\\).',
|
||||
options: [
|
||||
{ text: '\\(-154\\)', correct: true },
|
||||
{ text: '\\(154\\)', correct: false },
|
||||
{ text: '\\(-110\\)', correct: false },
|
||||
{ text: '\\(-180\\)', correct: false },
|
||||
],
|
||||
explanation: 'Пусть \\(t=\\log_3(x+12)\\): \\(-2<t<3\\Rightarrow -11{,}88<x<15\\). Целые: от −11 до 14. \\(-11\\times14=-154\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Неравенства', difficulty: 2,
|
||||
text: 'Решите неравенство \\(|2x-3|\\le7\\) и укажите число целых решений.',
|
||||
options: [
|
||||
{ text: '\\(8\\)', correct: true },
|
||||
{ text: '\\(7\\)', correct: false },
|
||||
{ text: '\\(9\\)', correct: false },
|
||||
{ text: '\\(6\\)', correct: false },
|
||||
],
|
||||
explanation: '\\(-7\\le2x-3\\le7\\Rightarrow-2\\le x\\le5\\). Целые: \\(-2,-1,0,1,2,3,4,5\\) — 8 чисел.',
|
||||
},
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════
|
||||
РАЗДЕЛ 2 — ЦТ 2021, Вариант 1
|
||||
══════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
{
|
||||
topic: 'Геометрия', difficulty: 1, year: 2021,
|
||||
text: '[ЦТ 2021 · A1]\n\nТреугольник \\(ABC\\) — равнобедренный с основанием \\(AB\\). Угол при вершине \\(C = 56°\\). Найдите угол \\(BAC\\).',
|
||||
options: [
|
||||
{ text: '62°', correct: true },
|
||||
{ text: '68°', correct: false },
|
||||
{ text: '34°', correct: false },
|
||||
{ text: '64°', correct: false },
|
||||
{ text: '28°', correct: false },
|
||||
],
|
||||
explanation: '\\(2\\cdot\\angle BAC=180°-56°=124°\\Rightarrow\\angle BAC=62°\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Арифметика и степени', difficulty: 1, year: 2021,
|
||||
text: '[ЦТ 2021 · A2]\n\nСреди дробей \\(\\dfrac{13}{7};\\;\\dfrac{15}{7};\\;\\dfrac{30}{7};\\;\\dfrac{27}{7};\\;\\dfrac{18}{7}\\) укажите ту, которая равна \\(4\\dfrac{2}{7}\\).',
|
||||
options: [
|
||||
{ text: '\\(\\dfrac{13}{7}\\)', correct: false },
|
||||
{ text: '\\(\\dfrac{15}{7}\\)', correct: false },
|
||||
{ text: '\\(\\dfrac{30}{7}\\)', correct: true },
|
||||
{ text: '\\(\\dfrac{27}{7}\\)', correct: false },
|
||||
{ text: '\\(\\dfrac{18}{7}\\)', correct: false },
|
||||
],
|
||||
explanation: '\\(4\\dfrac{2}{7}=\\dfrac{30}{7}\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Уравнения', difficulty: 1, year: 2021,
|
||||
text: '[ЦТ 2021 · A3]\n\nУкажите пару, которая НЕ является решением \\(x+y=12\\): \\((3;9)\\), \\((-15;3)\\), \\((0;12)\\), \\((14;-2)\\), \\((6;6)\\).',
|
||||
options: [
|
||||
{ text: '\\((3;9)\\)', correct: false },
|
||||
{ text: '\\((-15;3)\\)', correct: true },
|
||||
{ text: '\\((0;12)\\)', correct: false },
|
||||
{ text: '\\((14;-2)\\)', correct: false },
|
||||
{ text: '\\((6;6)\\)', correct: false },
|
||||
],
|
||||
explanation: '\\(-15+3=-12\\neq12\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Неравенства', difficulty: 1, year: 2021,
|
||||
text: '[ЦТ 2021 · A4]\n\nСреди чисел \\(-7;-11;11;-1;0\\) укажите то, которое не меньше \\(-9\\) и не больше \\(-2\\).',
|
||||
options: [
|
||||
{ text: '\\(-7\\)', correct: true },
|
||||
{ text: '\\(-11\\)', correct: false },
|
||||
{ text: '\\(11\\)', correct: false },
|
||||
{ text: '\\(-1\\)', correct: false },
|
||||
{ text: '\\(0\\)', correct: false },
|
||||
],
|
||||
explanation: '\\(-9\\le-7\\le-2\\) — только \\(-7\\) подходит.',
|
||||
},
|
||||
{
|
||||
topic: 'Словесные задачи', difficulty: 1, year: 2021,
|
||||
text: '[ЦТ 2021 · A5]\n\nТочка \\(C\\) делит отрезок \\(AB\\) в отношении \\(5:3\\) от \\(A\\). Длина \\(AB=24\\). Найдите \\(CB\\).',
|
||||
options: [
|
||||
{ text: '\\(14{,}4\\)', correct: false },
|
||||
{ text: '\\(9{,}6\\)', correct: false },
|
||||
{ text: '\\(6\\)', correct: false },
|
||||
{ text: '\\(9\\)', correct: true },
|
||||
{ text: '\\(15\\)', correct: false },
|
||||
],
|
||||
explanation: '\\(CB=\\dfrac{3}{8}\\cdot24=9\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Словесные задачи', difficulty: 2, year: 2021,
|
||||
text: '[ЦТ 2021 · A6]\n\nВ магазин поступило 43 коробки по 110 пачек масла. Какое наименьшее количество пачек нужно продавать ежедневно, чтобы распродать за не более чем 60 дней?',
|
||||
options: [
|
||||
{ text: '78', correct: false },
|
||||
{ text: '81', correct: false },
|
||||
{ text: '79', correct: true },
|
||||
{ text: '83', correct: false },
|
||||
],
|
||||
explanation: '\\(43\\times110=4730\\). \\(\\lceil4730/60\\rceil=79\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Функции', difficulty: 2, year: 2021,
|
||||
text: '[ЦТ 2021 · A7]\n\nНайдите количество целых \\(x\\in[-6;6]\\), при которых \\(f(x)\\le-3\\) (по графику функции).',
|
||||
options: [
|
||||
{ text: '7', correct: true },
|
||||
{ text: '6', correct: false },
|
||||
{ text: '5', correct: false },
|
||||
{ text: '9', correct: false },
|
||||
],
|
||||
explanation: 'По графику: 7 целых значений.',
|
||||
},
|
||||
{
|
||||
topic: 'Функции', difficulty: 2, year: 2021,
|
||||
text: '[ЦТ 2021 · A8]\n\nУпростите \\(|a-6|-|a|\\) при \\(\\dfrac{1}{6}<a<\\dfrac{3}{8}\\).',
|
||||
options: [
|
||||
{ text: '\\(-6\\)', correct: false },
|
||||
{ text: '\\(2a+6\\)', correct: false },
|
||||
{ text: '\\(-2a-6\\)', correct: false },
|
||||
{ text: '\\(6-2a\\)', correct: true },
|
||||
],
|
||||
explanation: '\\(|a-6|=6-a\\) и \\(|a|=a\\). Результат: \\(6-a-a=6-2a\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Логарифмы', difficulty: 2, year: 2021,
|
||||
text: '[ЦТ 2021 · A9]\n\nЧему равно \\(\\log_798-\\log_78+\\log_7\\dfrac{4}{7}\\)?',
|
||||
options: [
|
||||
{ text: '\\(1\\)', correct: true },
|
||||
{ text: '\\(2\\)', correct: false },
|
||||
{ text: '\\(\\log_72\\)', correct: false },
|
||||
{ text: '\\(0\\)', correct: false },
|
||||
],
|
||||
explanation: '\\(\\log_7\\dfrac{98\\cdot4}{8\\cdot7}=\\log_77=1\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Словесные задачи', difficulty: 1, year: 2021,
|
||||
text: '[ЦТ 2021 · A10]\n\nВелосипедист в первый день проехал 52 км, во второй — на 15% меньше. Сколько км за два дня?',
|
||||
options: [
|
||||
{ text: '102,4', correct: false },
|
||||
{ text: '96,2', correct: true },
|
||||
{ text: '89', correct: false },
|
||||
{ text: '88,4', correct: false },
|
||||
],
|
||||
explanation: '\\(52\\times0{,}85=44{,}2\\). Итого: \\(52+44{,}2=96{,}2\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Уравнения', difficulty: 1, year: 2021,
|
||||
text: '[ЦТ 2021 · A11]\n\nНайдите произведение координат точки пересечения прямых \\(6x-y=4\\) и \\(y-18=0\\).',
|
||||
options: [
|
||||
{ text: '4', correct: false },
|
||||
{ text: '18', correct: false },
|
||||
{ text: '72', correct: false },
|
||||
{ text: '66', correct: true },
|
||||
],
|
||||
explanation: '\\(y=18\\Rightarrow6x=22\\Rightarrow x=\\tfrac{11}{3}\\). Произведение: \\(\\tfrac{11}{3}\\cdot18=66\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Функции', difficulty: 2, year: 2021, type: 'multiple',
|
||||
text: '[ЦТ 2021 · A12]\n\nУкажите номера чётных функций:\n1) \\(y=0{,}2x^2\\)\n2) \\(y=8^{\\tfrac{x^4-16}{2|x|}}\\)\n3) \\(y=-\\dfrac{3}{x}\\)\n4) \\(y=x^2-x+2\\)\n5) \\(y=\\sin2x\\)',
|
||||
options: [
|
||||
{ text: '1', correct: true },
|
||||
{ text: '2', correct: true },
|
||||
{ text: '3', correct: false },
|
||||
{ text: '4', correct: false },
|
||||
{ text: '5', correct: false },
|
||||
],
|
||||
explanation: '1) ✓ чётная. 2) ✓ показатель не меняется при \\(x\\to-x\\). 3) нечётная. 4) ни чётная ни нечётная. 5) нечётная.',
|
||||
},
|
||||
{
|
||||
topic: 'Геометрия', difficulty: 2, year: 2021,
|
||||
text: '[ЦТ 2021 · A13]\n\nПлощадь прямоугольного треугольника равна 2, радиус описанной окружности \\(R\\). Укажите верную формулу суммы катетов \\(a+b\\).\n1) \\(\\dfrac{R^2+4}{R}\\)\n2) \\(\\sqrt{R^2+2}\\)\n3) \\(2\\sqrt{R^2+4}\\)\n4) \\(\\dfrac{R^2+2}{R}\\)\n5) \\(2\\sqrt{R^2+2}\\)',
|
||||
options: [
|
||||
{ text: '1', correct: false },
|
||||
{ text: '2', correct: false },
|
||||
{ text: '3', correct: false },
|
||||
{ text: '4', correct: false },
|
||||
{ text: '5', correct: true },
|
||||
],
|
||||
explanation: '\\(c=2R,\\;ab=4\\). \\((a+b)^2=4R^2+8\\Rightarrow a+b=2\\sqrt{R^2+2}\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Геометрия', difficulty: 3, year: 2021,
|
||||
text: '[ЦТ 2021 · A14]\n\nПрямая треугольная призма \\(ABCA_1B_1C_1\\): \\(\\angle A=20°\\), \\(\\angle C=25°\\), радиус описанной окружности \\(\\sqrt{7}\\). Площадь грани \\(AA_1C_1C=2\\sqrt{35}\\). Найдите длину диагонали этой грани.',
|
||||
options: [
|
||||
{ text: '\\(3\\sqrt{3}\\)', correct: false },
|
||||
{ text: '\\(2\\sqrt{5}\\)', correct: false },
|
||||
{ text: '\\(2\\sqrt{6}\\)', correct: true },
|
||||
{ text: '\\(4\\sqrt{6}\\)', correct: false },
|
||||
],
|
||||
explanation: '\\(\\angle B=135°\\). \\(AC=\\sqrt{14}\\). \\(AA_1=\\sqrt{10}\\). Диагональ: \\(\\sqrt{14+10}=2\\sqrt{6}\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Квадратные уравнения', difficulty: 2, year: 2021,
|
||||
text: '[ЦТ 2021 · A15]\n\nПараболы \\(y=2x^2+bx+c\\) с нулями \\(x=3\\) и \\(x=4\\). Найдите \\(b+c\\).',
|
||||
options: [
|
||||
{ text: '12', correct: false },
|
||||
{ text: '5', correct: false },
|
||||
{ text: '10', correct: true },
|
||||
{ text: '14', correct: false },
|
||||
],
|
||||
explanation: '\\(b=-14,\\;c=24\\). \\(b+c=10\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Уравнения', difficulty: 2, year: 2021, type: 'multiple',
|
||||
text: '[ЦТ 2021 · A16]\n\nУкажите номера равносильных уравнений:\n1) \\((x-6)(x+6)=0\\)\n2) \\(\\sqrt{x+10}=2\\)\n3) \\(x^2+36=0\\)\n4) \\(\\dfrac{x-x^2-5}{4}+\\dfrac{x^2-x-3}{3}=\\dfrac{1}{4}\\)\n5) \\(|x|-6=0\\)',
|
||||
options: [
|
||||
{ text: '1', correct: true },
|
||||
{ text: '2', correct: false },
|
||||
{ text: '3', correct: false },
|
||||
{ text: '4', correct: false },
|
||||
{ text: '5', correct: true },
|
||||
],
|
||||
explanation: 'Ур. 1 и 5 имеют одинаковые решения \\(x=\\pm6\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Геометрия', difficulty: 2, year: 2021,
|
||||
text: '[ЦТ 2021 · A17]\n\nТочки \\(A\\) и \\(B\\) — соседние вершины квадрата; \\(\\Delta x=7\\), \\(\\Delta y=2\\). Найдите площадь квадрата \\(ABCD\\).',
|
||||
options: [
|
||||
{ text: '37', correct: false },
|
||||
{ text: '14', correct: false },
|
||||
{ text: '50', correct: false },
|
||||
{ text: '53', correct: true },
|
||||
],
|
||||
explanation: '\\(AB^2=7^2+2^2=53\\). Площадь \\(=AB^2=53\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Геометрия', difficulty: 3, year: 2021,
|
||||
text: '[ЦТ 2021 · A18]\n\nПравильная четырёхугольная пирамида \\(SABCD\\), все рёбра 48. \\(M\\) — середина \\(SD\\), \\(N\\in SC\\), \\(CN:NS=1:3\\). Плоскость через \\(M,N\\), параллельная \\(SA\\), пересекает основание по отрезку длиной…',
|
||||
options: [
|
||||
{ text: '\\(16\\sqrt{13}\\)', correct: false },
|
||||
{ text: '\\(16\\sqrt{10}\\)', correct: true },
|
||||
{ text: '\\(8\\sqrt{37}\\)', correct: false },
|
||||
{ text: '\\(12\\sqrt{17}\\)', correct: false },
|
||||
],
|
||||
explanation: 'Ответ: \\(16\\sqrt{10}\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Статистика и диаграммы', difficulty: 1, year: 2021,
|
||||
text: '[ЦТ 2021 · B1]\n\nПо диаграмме посещений сайта (вт≈400, ср≈440, чт≈260, пт≈280, сб≈640, вс≈650) установите соответствие:\n\nА) В какой день было на 20 посещений больше предыдущего?\nБ) В какой день — на 35% меньше вторника?\nВ) В какой день — на 10% больше предыдущего?',
|
||||
options: [
|
||||
{ text: 'А4Б3В2', correct: true },
|
||||
{ text: 'А2Б3В4', correct: false },
|
||||
{ text: 'А4Б2В3', correct: false },
|
||||
{ text: 'А3Б4В2', correct: false },
|
||||
],
|
||||
explanation: 'А) пятница(+20 от чт)→4; Б) 400×0,65=260=чт→3; В) 400×1,1=440=ср→2. Ответ: А4Б3В2.',
|
||||
},
|
||||
{
|
||||
topic: 'Тригонометрия', difficulty: 3, year: 2021, type: 'multiple',
|
||||
text: '[ЦТ 2021 · B2]\n\nВыберите три верных утверждения:\n1) если \\(\\cos(\\arccos a)=\\cos(\\arccos\\tfrac{1}{18})\\), то \\(a=\\tfrac{1}{18}\\)\n2) если \\(\\cos a=-\\cos\\tfrac{\\pi}{18}\\), то \\(\\arccos(\\cos a)=-\\tfrac{\\pi}{18}\\)\n3) если \\(\\sin a=\\sin\\tfrac{17\\pi}{18}\\), то \\(\\arcsin(\\sin a)=\\tfrac{17\\pi}{18}\\)\n4) если \\(\\arccos a=\\tfrac{\\pi}{18}\\), то \\(a=\\cos\\tfrac{\\pi}{18}\\)\n5) если \\(\\sin a=\\sin\\tfrac{\\pi}{18}\\), то \\(a=-\\tfrac{\\pi}{18}\\)\n6) если \\(a=\\tfrac{\\pi}{18}\\) и \\(\\sin a=\\sin\\tfrac{\\pi}{18}\\), то \\(\\arcsin(\\sin a)=\\tfrac{\\pi}{18}\\)',
|
||||
options: [
|
||||
{ text: '1', correct: true },
|
||||
{ text: '2', correct: false },
|
||||
{ text: '3', correct: false },
|
||||
{ text: '4', correct: true },
|
||||
{ text: '5', correct: false },
|
||||
{ text: '6', correct: true },
|
||||
],
|
||||
explanation: '1) ✓, 4) ✓ по определению arccos, 6) ✓ т.к. \\(\\tfrac{\\pi}{18}\\in[-\\tfrac{\\pi}{2};\\tfrac{\\pi}{2}]\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Геометрия', difficulty: 3, year: 2021, type: 'multiple',
|
||||
text: '[ЦТ 2021 · B3]\n\nПлоскости \\(\\alpha\\perp\\beta\\), пересекаются по \\(a\\), точка \\(A\\in\\beta\\). Выберите три верных утверждения:\n1) любая прямая через A, пересекающая α, пересекает a\n2) единственная прямая через A ⊥ α\n3) прямая через A ⊥ β тоже ⊥ α\n4) любая точка a ∈ α и β\n5) любая прямая в α ⊥ a тоже ⊥ β\n6) любая прямая ⊥ a принадлежит β',
|
||||
options: [
|
||||
{ text: '1', correct: true },
|
||||
{ text: '2', correct: false },
|
||||
{ text: '3', correct: false },
|
||||
{ text: '4', correct: true },
|
||||
{ text: '5', correct: true },
|
||||
{ text: '6', correct: false },
|
||||
],
|
||||
explanation: '1) ✓, 4) ✓ — по определению линии пересечения, 5) ✓ — признак перпендикулярности плоскостей.',
|
||||
},
|
||||
{
|
||||
topic: 'Словесные задачи', difficulty: 2, year: 2021,
|
||||
text: '[ЦТ 2021 · B4]\n\nНа пастбище (квадрат) загон для скота занимает \\(\\tfrac{1}{32}\\) площади пастбища. Найдите площадь загона (в м²), если по рисунку ширина пастбища связана с размером загона.',
|
||||
options: [
|
||||
{ text: '800', correct: true },
|
||||
{ text: '640', correct: false },
|
||||
{ text: '900', correct: false },
|
||||
{ text: '1000', correct: false },
|
||||
],
|
||||
explanation: '\\(S_{\\text{паст}}=32\\cdot S_{\\text{загон}}\\). При \\(a^2=800\\): площадь загона = 800 м².',
|
||||
},
|
||||
{
|
||||
topic: 'Арифметика и степени', difficulty: 2, year: 2021,
|
||||
text: '[ЦТ 2021 · B5]\n\nНайдите \\(\\sqrt{8}\\cdot\\sqrt[3]{-7}\\cdot\\sqrt{32}\\cdot\\sqrt[3]{49}-7\\cdot\\dfrac{\\sqrt[3]{64}}{-2}\\).',
|
||||
options: [
|
||||
{ text: '\\(-98\\)', correct: true },
|
||||
{ text: '\\(-126\\)', correct: false },
|
||||
{ text: '\\(-84\\)', correct: false },
|
||||
{ text: '\\(-70\\)', correct: false },
|
||||
],
|
||||
explanation: '\\(\\sqrt{8}\\cdot\\sqrt{32}=16\\). \\(\\sqrt[3]{-7\\cdot49}=-7\\). Первое: \\(-112\\). Второе: \\(7\\cdot4/(-2)=-14\\). \\(-112-(-14)=-98\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Геометрия', difficulty: 2, year: 2021,
|
||||
text: '[ЦТ 2021 · B6]\n\nПлощадь боковой поверхности цилиндра \\(=15\\pi\\), радиус на 3,5 больше высоты. Найдите \\(\\dfrac{6V}{\\pi}\\).',
|
||||
options: [
|
||||
{ text: '225', correct: true },
|
||||
{ text: '150', correct: false },
|
||||
{ text: '300', correct: false },
|
||||
{ text: '180', correct: false },
|
||||
],
|
||||
explanation: '\\(rh=7{,}5,\\;r=h+3{,}5\\Rightarrow h=1{,}5,\\;r=5\\). \\(V=37{,}5\\pi\\). \\(6V/\\pi=225\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Тригонометрия', difficulty: 3, year: 2021,
|
||||
text: '[ЦТ 2021 · B7]\n\nРешите \\(\\sqrt{3}\\cos\\!\\left(\\dfrac{5\\pi}{18}+\\pi x\\right)=-1{,}5\\). Найдите: 3 × наибольший корень на [3;9] × количество корней на [3;9].',
|
||||
options: [
|
||||
{ text: '160', correct: true },
|
||||
{ text: '120', correct: false },
|
||||
{ text: '200', correct: false },
|
||||
{ text: '90', correct: false },
|
||||
],
|
||||
explanation: 'На [3;9]: 6 корней, \\(x_{\\max}=\\tfrac{80}{9}\\). Ответ: \\(3\\cdot\\tfrac{80}{9}\\cdot6=160\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Логарифмы', difficulty: 3, year: 2021,
|
||||
text: '[ЦТ 2021 · B8]\n\nНайдите сумму всех целых решений \\(\\log_{0{,}3}\\log_{4{,}7}(2^{x+9{,}1}-1)\\ge0\\).',
|
||||
options: [
|
||||
{ text: '\\(-15\\)', correct: true },
|
||||
{ text: '\\(-10\\)', correct: false },
|
||||
{ text: '\\(-21\\)', correct: false },
|
||||
{ text: '\\(-6\\)', correct: false },
|
||||
],
|
||||
explanation: 'Целые решения: \\(x=-8\\) и \\(x=-7\\). Сумма: \\(-15\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Геометрия', difficulty: 3, year: 2021,
|
||||
text: '[ЦТ 2021 · B9]\n\n\\(AC\\) — общая гипотенуза прямоугольных треугольников \\(ABC\\) и \\(ADC\\), плоскости ⊥. \\(AB=9\\sqrt{3}\\), \\(BC=9\\sqrt{5}\\), \\(AD=DC\\). Найдите \\(BD^2\\).',
|
||||
options: [
|
||||
{ text: '324', correct: true },
|
||||
{ text: '256', correct: false },
|
||||
{ text: '400', correct: false },
|
||||
{ text: '289', correct: false },
|
||||
],
|
||||
explanation: '\\(AC=18\\sqrt{2}\\), \\(AD=18\\). \\(BD^2=324\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Прогрессии', difficulty: 2, year: 2021,
|
||||
text: '[ЦТ 2021 · B10]\n\n\\(a_n=2n^2-15n\\). Найдите наименьший член \\(a_m\\) и его номер \\(m\\). Запишите \\(m\\cdot a_m\\).',
|
||||
options: [
|
||||
{ text: '\\(-112\\)', correct: true },
|
||||
{ text: '\\(-98\\)', correct: false },
|
||||
{ text: '\\(-128\\)', correct: false },
|
||||
{ text: '\\(-80\\)', correct: false },
|
||||
],
|
||||
explanation: '\\(a_4=-28\\) — минимум. \\(4\\times(-28)=-112\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Уравнения', difficulty: 3, year: 2021,
|
||||
text: '[ЦТ 2021 · B11]\n\nНайдите \\(25\\cdot(x_1^2+x_2^2)\\), где \\(x_1,x_2\\) — корни уравнения \\(10\\sqrt{\\dfrac{x^2}{14+5x-x^2}}-2\\sqrt{\\dfrac{14+5x-x^2}{x^2}}=19\\).',
|
||||
options: [
|
||||
{ text: '960', correct: true },
|
||||
{ text: '800', correct: false },
|
||||
{ text: '1200', correct: false },
|
||||
{ text: '750', correct: false },
|
||||
],
|
||||
explanation: '\\(t=2\\Rightarrow5x^2-20x-56=0\\). \\(x_1^2+x_2^2=\\tfrac{192}{5}\\). \\(25\\cdot\\tfrac{192}{5}=960\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Геометрия', difficulty: 3, year: 2021,
|
||||
text: '[ЦТ 2021 · B12]\n\nПрямая через вершину \\(K\\) треугольника \\(KMN\\) делит медиану \\(MA\\) в отношении \\(8:3\\) от \\(M\\) и пересекает \\(MN\\) в точке \\(B\\). \\(S_{KMB}=16\\). Найдите \\(S_{KMN}\\).',
|
||||
options: [
|
||||
{ text: '28', correct: true },
|
||||
{ text: '24', correct: false },
|
||||
{ text: '32', correct: false },
|
||||
{ text: '36', correct: false },
|
||||
],
|
||||
explanation: '\\(MB:BN=8:3\\Rightarrow MN/MB=11/8\\). \\(S_{KMN}=28\\).',
|
||||
},
|
||||
{
|
||||
topic: 'Теория чисел', difficulty: 3, year: 2021,
|
||||
text: '[ЦТ 2021 · B13]\n\nПетя записал два различных натуральных числа, сложил, перемножил, вычел меньшее из большего, разделил большее на меньшее. Сумма четырёх результатов = 1521. Найдите сумму всех таких пар.',
|
||||
options: [
|
||||
{ text: '460', correct: true },
|
||||
{ text: '390', correct: false },
|
||||
{ text: '520', correct: false },
|
||||
{ text: '410', correct: false },
|
||||
],
|
||||
explanation: 'Пары: (108,12) и (338,2). Суммы: 120+340=460.',
|
||||
},
|
||||
{
|
||||
topic: 'Геометрия', difficulty: 3, year: 2021,
|
||||
text: '[ЦТ 2021 · B14]\n\nПирамида \\(SABCD\\): \\(AO=9\\), \\(OC=16\\), \\(BO=OD=12\\), диагонали ⊥. Вершина \\(S\\) удалена на \\(\\tfrac{61}{7}\\) от каждой стороны основания. Через середину высоты параллельно основанию проведена плоскость. Найдите \\(10\\cdot V_{\\text{большей части}}\\).',
|
||||
options: [
|
||||
{ text: '1375', correct: true },
|
||||
{ text: '1250', correct: false },
|
||||
{ text: '1500', correct: false },
|
||||
{ text: '1100', correct: false },
|
||||
],
|
||||
explanation: '\\(S_{ABCD}=300\\), \\(h=\\tfrac{11}{7}\\), \\(V=\\tfrac{1100}{7}\\). Большая часть: \\(\\tfrac{1925}{14}\\). \\(10V=1375\\).',
|
||||
},
|
||||
];
|
||||
|
||||
/* ── Вставка (идемпотентно — пропускать уже существующие) ─────────────── */
|
||||
let inserted = 0, skipped = 0;
|
||||
const checkQ = db.prepare('SELECT id FROM questions WHERE subject_id=? AND text=?');
|
||||
const insertQ = db.prepare(
|
||||
'INSERT INTO questions (subject_id, topic_id, text, difficulty, year, explanation, type) VALUES (?,?,?,?,?,?,?)'
|
||||
);
|
||||
const insertO = db.prepare(
|
||||
'INSERT INTO options (question_id, text, is_correct, order_index) VALUES (?,?,?,?)'
|
||||
);
|
||||
|
||||
db.exec('BEGIN');
|
||||
try {
|
||||
for (const q of questions) {
|
||||
if (checkQ.get(SID, q.text)) { skipped++; continue; }
|
||||
const topicId = getOrCreateTopic(SID, q.topic);
|
||||
const { lastInsertRowid: qid } = insertQ.run(
|
||||
SID, topicId, q.text, q.difficulty,
|
||||
q.year || null, q.explanation || null, q.type || 'single'
|
||||
);
|
||||
q.options.forEach((o, i) => insertO.run(qid, o.text, o.correct ? 1 : 0, i));
|
||||
inserted++;
|
||||
}
|
||||
db.exec('COMMIT');
|
||||
console.log(`✓ math: вставлено ${inserted}, пропущено ${skipped} (уже было).`);
|
||||
} catch (err) {
|
||||
db.exec('ROLLBACK');
|
||||
console.error('Ошибка:', err.message);
|
||||
process.exit(1);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,405 @@
|
||||
/**
|
||||
* seed-red-book-extra.js
|
||||
* Добавляет дополнительные виды, сезонность и пищевые связи.
|
||||
* Запуск: node src/db/seed-red-book-extra.js (из папки backend/)
|
||||
* Идемпотентен — не дублирует существующие виды.
|
||||
*/
|
||||
require('./migrate');
|
||||
const db = require('./db');
|
||||
|
||||
/* ── helpers ── */
|
||||
const groupRow = (name) => db.prepare("SELECT id FROM rb_groups WHERE name_ru = ?").get(name);
|
||||
const habitatRow = (type) => db.prepare("SELECT id FROM rb_habitats WHERE type = ?").get(type);
|
||||
|
||||
const insSp = db.prepare(`
|
||||
INSERT INTO rb_species
|
||||
(group_id, habitat_id, name_ru, name_be, name_lat, category, by_category,
|
||||
description, interesting_fact, threats, conservation, where_to_see,
|
||||
photo_url, model_type, population_trend, biomass_kg, season_active)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
`);
|
||||
const insReg = db.prepare('INSERT OR IGNORE INTO rb_species_regions (species_id, region_code) VALUES (?,?)');
|
||||
const insWeb = db.prepare('INSERT OR IGNORE INTO rb_food_web (predator_id, prey_id, strength) VALUES (?,?,?)');
|
||||
|
||||
function addSp(data) {
|
||||
// Guard: skip if already exists by name_ru
|
||||
const existing = db.prepare("SELECT id FROM rb_species WHERE name_ru = ?").get(data.name_ru);
|
||||
if (existing) return existing.id;
|
||||
|
||||
const gid = groupRow(data.group)?.id;
|
||||
const hid = habitatRow(data.habitat)?.id || null;
|
||||
if (!gid) { console.warn(`Group not found: ${data.group}`); return null; }
|
||||
|
||||
const id = insSp.run(
|
||||
gid, hid,
|
||||
data.name_ru, data.name_be || '', data.name_lat || '',
|
||||
data.category, data.by_category || 'III',
|
||||
data.description || '', data.fact || '',
|
||||
JSON.stringify(data.threats || []),
|
||||
data.conservation || '', data.where_to_see || '',
|
||||
data.photo || '', data.model || 'silhouette',
|
||||
JSON.stringify(data.trend || []),
|
||||
data.biomass || 0,
|
||||
JSON.stringify(data.seasons || []),
|
||||
).lastInsertRowid;
|
||||
|
||||
(data.regions || []).forEach(r => insReg.run(id, r));
|
||||
return id;
|
||||
}
|
||||
|
||||
/* ── existing ids by name ── */
|
||||
function getId(name) {
|
||||
const r = db.prepare("SELECT id FROM rb_species WHERE name_ru = ?").get(name);
|
||||
return r?.id || null;
|
||||
}
|
||||
|
||||
db.exec('BEGIN');
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
НОВЫЕ ВИДЫ — ПТИЦЫ
|
||||
══════════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
const pukhlyak = addSp({
|
||||
group: 'Птицы', habitat: 'conifer',
|
||||
name_ru: 'Пухляк (гаичка буроголовая)', name_be: 'Мухаловка шэрая', name_lat: 'Poecile montanus',
|
||||
category: 'NT', by_category: 'IV',
|
||||
description: 'Небольшая птица хвойных лесов, запасающая корм в тайниках на зиму. Способна запомнить сотни тайников одновременно. Активна круглый год, не покидает территорию даже в сильные морозы.',
|
||||
fact: 'Пухляк запасает до 2000 семян за осень, запоминая каждое место с точностью до нескольких сантиметров.',
|
||||
threats: ['Вырубка ельников', 'Конкуренция с синицами'],
|
||||
conservation: 'Сохранение разновозрастных хвойных лесов.',
|
||||
where_to_see: 'Беловежская пуща, Налибокская пуща, Нарочанский НП',
|
||||
biomass: 0.012, seasons: ['10','11','12','1','2','3'],
|
||||
regions: ['vitebsk','minsk','grodno'],
|
||||
});
|
||||
|
||||
const lebed = addSp({
|
||||
group: 'Птицы', habitat: 'river',
|
||||
name_ru: 'Лебедь-шипун', name_be: 'Лебедзь-шыпун', name_lat: 'Cygnus olor',
|
||||
category: 'NT', by_category: 'IV',
|
||||
description: 'Один из крупнейших летающих птиц мира — масса до 14 кг. Агрессивно защищает гнездо от любых нарушителей. Пары создаются на много лет. В Беларуси гнездится на озёрах, прудах и медленных реках.',
|
||||
fact: 'Лебедь-шипун — самая тяжёлая из всех летающих птиц. Чтобы взлететь, ему нужно пробежать по воде 40–50 м.',
|
||||
threats: ['Рекреационная нагрузка', 'Свинцовое отравление', 'Беспокойство у гнёзд'],
|
||||
conservation: 'Охранные зоны у гнёзд. Ограничение водно-моторного спорта.',
|
||||
where_to_see: 'Браславские озёра, Нарочанский НП, Минское море',
|
||||
biomass: 10.0, seasons: ['4','5','6','7','8'],
|
||||
regions: ['vitebsk','minsk','brest','grodno'],
|
||||
});
|
||||
|
||||
const klikun = addSp({
|
||||
group: 'Птицы', habitat: 'river',
|
||||
name_ru: 'Лебедь-кликун', name_be: 'Лебедзь-крыкун', name_lat: 'Cygnus cygnus',
|
||||
category: 'VU', by_category: 'II',
|
||||
description: 'Перелётный лебедь, зимующий в Беларуси. В отличие от шипуна держит шею прямо. Мощный трубный крик слышен далеко. Небольшая гнездовая популяция в Витебской области значительно выросла за последние 30 лет.',
|
||||
fact: 'Лебедь-кликун мигрирует на высоте до 8000 м. Зафиксированы особи, долетавшие из Западной Сибири.',
|
||||
threats: ['Беспокойство', 'Отравление свинцом', 'Недостаток спокойных зимовальных мест'],
|
||||
conservation: 'Охранные зоны. Запрет охоты.',
|
||||
where_to_see: 'Браславские озёра (зимовка), Витебская область (гнездование)',
|
||||
biomass: 9.0, seasons: ['10','11','12','1','2','3'],
|
||||
regions: ['vitebsk','minsk'],
|
||||
});
|
||||
|
||||
const domovoy_sych = addSp({
|
||||
group: 'Птицы', habitat: 'meadow',
|
||||
name_ru: 'Сыч домовый', name_be: 'Хатні сычык', name_lat: 'Athene noctua',
|
||||
category: 'EN', by_category: 'II',
|
||||
description: 'Маленькая сова агроландшафтов, живущая в старых постройках, скалах и пнях. Активна в сумерках. В Беларуси редкость — известны единицы гнездовых пар. Численность катастрофически снизилась из-за химизации сельского хозяйства.',
|
||||
fact: 'Домовый сыч ловит до 600 мышей и 2000 насекомых в год, являясь природным регулятором грызунов.',
|
||||
threats: ['Применение пестицидов', 'Снижение числа старых построек', 'Кошки'],
|
||||
conservation: 'Установка гнездовых ящиков. Ограничение пестицидов.',
|
||||
where_to_see: 'Брестская область (единичные пары)',
|
||||
biomass: 0.18, seasons: ['3','4','5','6','7','8','9'],
|
||||
regions: ['brest','gomel'],
|
||||
});
|
||||
|
||||
const velik_kulak = addSp({
|
||||
group: 'Птицы', habitat: 'wetland',
|
||||
name_ru: 'Большой кроншнеп', name_be: 'Вялікі кроншнеп', name_lat: 'Numenius arquata',
|
||||
category: 'EN', by_category: 'II',
|
||||
description: 'Крупнейший кулик Беларуси с изогнутым книзу клювом. Гнездится на влажных лугах и болотах. Очень пуглив — уже с 500 метров покидает гнездо при появлении людей. Численность неуклонно сокращается.',
|
||||
fact: 'Большой кроншнеп может зондировать почву на глубину 15 см своим клювом, находя дождевых червей по осязанию.',
|
||||
threats: ['Ранний сенокос', 'Осушение лугов', 'Хищничество лисиц'],
|
||||
conservation: 'Отсрочка сенокоса. Сохранение пойменных лугов.',
|
||||
where_to_see: 'Пойма Припяти, Нарочанский НП',
|
||||
biomass: 0.7, seasons: ['4','5','6','7'],
|
||||
regions: ['brest','gomel','minsk','vitebsk'],
|
||||
});
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
НОВЫЕ ВИДЫ — МЛЕКОПИТАЮЩИЕ
|
||||
══════════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
const zaets = addSp({
|
||||
group: 'Млекопитающие', habitat: 'forest',
|
||||
name_ru: 'Заяц-беляк', name_be: 'Заяц-белы', name_lat: 'Lepus timidus',
|
||||
category: 'NT', by_category: 'IV',
|
||||
description: 'Самый «белорусский» заяц — зимой его мех белеет, что обеспечивает маскировку на снегу. Предпочитает смешанные леса и опушки. Численность регулярно колеблется с периодом 10–11 лет в зависимости от популяции хищников.',
|
||||
fact: 'Заяц-беляк может развивать скорость до 70 км/ч и прыгать на 4 м в длину, уходя от преследователей.',
|
||||
threats: ['Волки', 'Лисы', 'Охота', 'Уменьшение подлеска'],
|
||||
conservation: 'Регулирование охотничьей нагрузки.',
|
||||
where_to_see: 'Все крупные леса Беларуси',
|
||||
biomass: 3.5, seasons: ['11','12','1','2','3'],
|
||||
regions: ['vitebsk','minsk','grodno','brest','gomel','mogilev'],
|
||||
});
|
||||
|
||||
const lasitsa = addSp({
|
||||
group: 'Млекопитающие', habitat: 'meadow',
|
||||
name_ru: 'Ласка обыкновенная', name_be: 'Ласіца звычайная', name_lat: 'Mustela nivalis',
|
||||
category: 'NT', by_category: 'IV',
|
||||
description: 'Самый маленький хищник Беларуси весом 70–130 г. Способна следовать за мышами в их норы. Зимой меняет мех на белый. Настоящий «регулятор» популяций полёвок — за год уничтожает до 2000 грызунов.',
|
||||
fact: 'Ласка может убивать добычу, в 5 раз тяжелее себя — кроликов и крыс.',
|
||||
threats: ['Яды для грызунов', 'Уничтожение живых изгородей'],
|
||||
conservation: 'Ограничение использования родентицидов.',
|
||||
where_to_see: 'Луга и поля по всей Беларуси',
|
||||
biomass: 0.1, seasons: ['1','2','3','4','5','6','7','8','9','10','11','12'],
|
||||
regions: ['vitebsk','minsk','grodno','brest','gomel','mogilev'],
|
||||
});
|
||||
|
||||
const night_bat = addSp({
|
||||
group: 'Млекопитающие', habitat: 'forest',
|
||||
name_ru: 'Широкоушка европейская', name_be: 'Шырокавухая кажан', name_lat: 'Barbastella barbastellus',
|
||||
category: 'EN', by_category: 'II',
|
||||
description: 'Редкая летучая мышь с характерными широкими ушами. Зимует в пещерах и подвалах. Охотится на ночных бабочек, улавливая их вибрации. Одна из наиболее уязвимых рукокрылых Европы.',
|
||||
fact: 'Широкоушка способна поймать до 2000 насекомых за ночь. Её эхолокация настолько совершенна, что она не задевает паутину.',
|
||||
threats: ['Утепление зданий', 'Химические обработки леса', 'Беспокойство в зимовочных убежищах'],
|
||||
conservation: 'Охрана зимовочных убежищ. Вывешивание летних домиков.',
|
||||
where_to_see: 'Беловежская пуща, старые здания Гродненской области',
|
||||
biomass: 0.01, seasons: ['4','5','6','7','8','9'],
|
||||
regions: ['grodno','brest','minsk'],
|
||||
});
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
НОВЫЕ ВИДЫ — РАСТЕНИЯ
|
||||
══════════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
const kupena = addSp({
|
||||
group: 'Растения', habitat: 'forest',
|
||||
name_ru: 'Купена многоцветковая', name_be: 'Купена шматкветкавая', name_lat: 'Polygonatum multiflorum',
|
||||
category: 'NT', by_category: 'IV',
|
||||
description: 'Изящное тенелюбивое растение тёмных лесов с белыми колокольчатыми цветами. Плоды — тёмно-синие ягоды — ядовиты для человека, но поедаются птицами. Медленно растёт: возраст крупных особей может достигать 50 лет.',
|
||||
fact: 'Купена цветёт только один раз в жизни. Название происходит от «купель» — ранее растение применялось в народной медицине.',
|
||||
threats: ['Сбор на букеты', 'Рекреационная нагрузка', 'Вырубка лесов'],
|
||||
conservation: 'Запрет сбора. Включение в охраняемые участки.',
|
||||
where_to_see: 'Беловежская пуща, дубравы Полесья',
|
||||
biomass: 0.05, seasons: ['4','5','6'],
|
||||
regions: ['grodno','brest','gomel','minsk'],
|
||||
});
|
||||
|
||||
const kruzhevnitsa = addSp({
|
||||
group: 'Растения', habitat: 'wetland',
|
||||
name_ru: 'Альдрованда пузырчатая', name_be: 'Альдрованда бурбалкавая', name_lat: 'Aldrovanda vesiculosa',
|
||||
category: 'CR', by_category: 'I',
|
||||
description: 'Единственное в мире водное хищное растение, не имеющее корней. Ловит водных беспозвоночных ловушками, напоминающими венерину мухоловку. Популяции в Беларуси — единственные в центральной Европе. Крайне чувствительна к загрязнению воды.',
|
||||
fact: 'Альдрованда — самое быстрое хищное растение в воде: захлопывает ловушку за 0.01 секунды.',
|
||||
threats: ['Загрязнение воды', 'Эвтрофикация', 'Зарастание водоёмов'],
|
||||
conservation: 'Охрана болотных водоёмов. Восстановление популяций.',
|
||||
where_to_see: 'Ольманские болота, Брестская область',
|
||||
biomass: 0.001, seasons: ['6','7','8'],
|
||||
regions: ['brest'],
|
||||
});
|
||||
|
||||
const korolev_papor = addSp({
|
||||
group: 'Растения', habitat: 'forest',
|
||||
name_ru: 'Папоротник-орляк', name_be: 'Арляк звычайны', name_lat: 'Pteridium aquilinum',
|
||||
category: 'NT', by_category: 'IV',
|
||||
description: 'Один из древнейших видов флоры Беларуси — существует без изменений 55 миллионов лет. Образует обширные куртины в сосновых лесах. Молодые побеги («скрипухи») ядовиты для скота, но съедобны для людей после варки.',
|
||||
fact: 'Орляк — один из немногих папоротников, покрывающих более 1% суши Земли. Его корневища уходят на глубину 4 м.',
|
||||
threats: ['Интенсивные рубки', 'Пожары', 'Иссушение лесов'],
|
||||
conservation: 'Охрана старых лесов.',
|
||||
where_to_see: 'Хвойные леса по всей Беларуси',
|
||||
biomass: 0.3, seasons: ['5','6','7','8','9'],
|
||||
regions: ['vitebsk','minsk','grodno','brest','gomel','mogilev'],
|
||||
});
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
НОВЫЕ ВИДЫ — НАСЕКОМЫЕ
|
||||
══════════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
const rosalia = addSp({
|
||||
group: 'Насекомые', habitat: 'forest',
|
||||
name_ru: 'Усач альпийский', name_be: 'Вусач альпійскі', name_lat: 'Rosalia alpina',
|
||||
category: 'EN', by_category: 'II',
|
||||
description: 'Один из красивейших жуков Европы — голубовато-серый с чёрными пятнами, с усами длиннее тела. Личинка живёт в мёртвой буковой древесине. В Беларуси встречается на границе ареала, в Беловежской пуще.',
|
||||
fact: 'Усач альпийский — один из символов охраны природы Европы. Его изображение есть на почтовых марках многих стран.',
|
||||
threats: ['Вырубка старых буков', 'Удаление мёртвой древесины'],
|
||||
conservation: 'Сохранение старых буковых деревьев и валежника.',
|
||||
where_to_see: 'Беловежская пуща',
|
||||
biomass: 0.003, seasons: ['6','7','8'],
|
||||
regions: ['brest'],
|
||||
});
|
||||
|
||||
const pchela_plothnik = addSp({
|
||||
group: 'Насекомые', habitat: 'forest',
|
||||
name_ru: 'Пчела-плотник', name_be: 'Пчала-сталяр', name_lat: 'Xylocopa violacea',
|
||||
category: 'VU', by_category: 'III',
|
||||
description: 'Крупнейшая дикая пчела Беларуси — тело до 28 мм с фиолетово-синим блеском крыльев. Не живёт в ульях — самостоятельно прогрызает норы в сухой древесине. Важный опылитель с длинным хоботком для глубоких цветков.',
|
||||
fact: 'Пчела-плотник способна прогрызть тоннель длиной 30 см в дубовом бревне.',
|
||||
threats: ['Уборка старой и мёртвой древесины', 'Применение инсектицидов'],
|
||||
conservation: 'Сохранение старых деревьев. Установка гнездовых брёвен.',
|
||||
where_to_see: 'Беловежская пуща, старые сады Брестской области',
|
||||
biomass: 0.001, seasons: ['4','5','6','7','8'],
|
||||
regions: ['brest','grodno'],
|
||||
});
|
||||
|
||||
const zelenyi_metallik = addSp({
|
||||
group: 'Насекомые', habitat: 'meadow',
|
||||
name_ru: 'Бронзовка гладкая', name_be: 'Бронзаўка гладкая', name_lat: 'Protaetia aeruginosa',
|
||||
category: 'EN', by_category: 'II',
|
||||
description: 'Жук с изумрудным металлическим блеском. Личинка развивается в дуплах старых дубов, питаясь перегноем. Взрослые особи питаются цветочным нектаром. Исчезает вместе с вековыми дубами.',
|
||||
fact: 'Бронзовка — «инженер экосистем»: личинки рыхлят перегной в дуплах, создавая субстрат для редких растений и других насекомых.',
|
||||
threats: ['Вырубка старых дубов', 'Исчезновение дупел', 'Применение инсектицидов'],
|
||||
conservation: 'Сохранение старовозрастных дубрав.',
|
||||
where_to_see: 'Беловежская пуща, дубравы Налибокской пущи',
|
||||
biomass: 0.002, seasons: ['5','6','7'],
|
||||
regions: ['brest','grodno','minsk'],
|
||||
});
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
НОВЫЕ ВИДЫ — РЫБЫ
|
||||
══════════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
const som = addSp({
|
||||
group: 'Рыбы', habitat: 'river',
|
||||
name_ru: 'Сом обыкновенный', name_be: 'Сом звычайны', name_lat: 'Silurus glanis',
|
||||
category: 'NT', by_category: 'IV',
|
||||
description: 'Крупнейшая пресноводная рыба Беларуси — достигает 3 м длины и 200 кг. Ведёт ночной образ жизни. Охотится в основном на рыбу, но не брезгует птицами и млекопитающими. В старых реках встречались особи возрастом 60–80 лет.',
|
||||
fact: 'Сом может выползать на берег, чтобы охотиться на купающихся уток и голубей — этот феномен наблюдали на р. Биже в Испании.',
|
||||
threats: ['Браконьерство', 'Ухудшение качества воды', 'Перекрытие миграционных путей'],
|
||||
conservation: 'Ограничение промысла. Охрана нерестилищ.',
|
||||
where_to_see: 'Припять, Нёман, Днепр, Западная Двина',
|
||||
biomass: 30.0, seasons: ['5','6','7','8','9'],
|
||||
regions: ['brest','gomel','grodno','minsk','vitebsk','mogilev'],
|
||||
});
|
||||
|
||||
const taimen = addSp({
|
||||
group: 'Рыбы', habitat: 'river',
|
||||
name_ru: 'Хариус европейский', name_be: 'Харыус еўрапейскі', name_lat: 'Thymallus thymallus',
|
||||
category: 'VU', by_category: 'III',
|
||||
description: 'Стремительная рыба холодных чистых рек с высоким, похожим на парус, спинным плавником. Индикатор качества воды — исчезает при малейшем загрязнении. Питается насекомыми, упавшими на поверхность воды.',
|
||||
fact: 'Хариус умеет «читать» поверхность воды: он точно определяет, куда упадёт насекомое, и занимает позицию заблаговременно.',
|
||||
threats: ['Загрязнение рек', 'Чрезмерный вылов', 'Мелиорация'],
|
||||
conservation: 'Охрана чистых рек. Запрет химических обработок водосборов.',
|
||||
where_to_see: 'Западная Двина (верховья), Вилия, Нёман (верховья)',
|
||||
biomass: 0.3, seasons: ['4','5','6'],
|
||||
regions: ['vitebsk','grodno'],
|
||||
});
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
НОВЫЕ ВИДЫ — РЕПТИЛИИ И АМФИБИИ
|
||||
══════════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
const gadyuka = addSp({
|
||||
group: 'Рептилии и амфибии', habitat: 'wetland',
|
||||
name_ru: 'Гадюка обыкновенная', name_be: 'Гадзюка звычайная', name_lat: 'Vipera berus',
|
||||
category: 'NT', by_category: 'IV',
|
||||
description: 'Единственная ядовитая змея Беларуси. Укус редко опасен для взрослого здорового человека, но вызывает болезненный отёк. Питается грызунами, лягушками и ящерицами. Незаменима в экосистеме как регулятор численности грызунов.',
|
||||
fact: 'Гадюка рожает живых детёнышей (до 12 штук), а не откладывает яйца. Молодые змеи с первого дня ядовиты.',
|
||||
threats: ['Уничтожение людьми', 'Осушение болот', 'Автодороги'],
|
||||
conservation: 'Просветительская работа. Охрана болотных участков.',
|
||||
where_to_see: 'Болота и леса по всей Беларуси',
|
||||
biomass: 0.15, seasons: ['4','5','6','7','8','9'],
|
||||
regions: ['vitebsk','minsk','grodno','brest','gomel','mogilev'],
|
||||
});
|
||||
|
||||
const listvy_trit = addSp({
|
||||
group: 'Рептилии и амфибии', habitat: 'wetland',
|
||||
name_ru: 'Гребенчатый тритон', name_be: 'Грабенчасты трытон', name_lat: 'Triturus cristatus',
|
||||
category: 'NT', by_category: 'IV',
|
||||
description: 'Крупнейший тритон Беларуси. В брачный период самец развивает зубчатый гребень вдоль всего тела. Обитает во влажных лесах и болотах. Выделяет ядовитые вещества через кожу для защиты от хищников.',
|
||||
fact: 'Гребенчатый тритон может прожить 20–25 лет. В воде он дышит через кожу, не нуждаясь в воздухе часами.',
|
||||
threats: ['Осушение водоёмов', 'Хищники', 'Загрязнение'],
|
||||
conservation: 'Охрана болотных водоёмов. Создание пруд-убежищ.',
|
||||
where_to_see: 'Пуща Налибокская, лесные водоёмы Витебской области',
|
||||
biomass: 0.02, seasons: ['3','4','5','6'],
|
||||
regions: ['vitebsk','minsk','grodno'],
|
||||
});
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
НОВЫЕ ВИДЫ — ГРИБЫ
|
||||
══════════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
const hleb_grib = addSp({
|
||||
group: 'Грибы', habitat: 'forest',
|
||||
name_ru: 'Ёж гриб (герций)', name_be: 'Гербій жаўтлявы', name_lat: 'Hericium erinaceus',
|
||||
category: 'EN', by_category: 'II',
|
||||
description: 'Необычный гриб с длинными белыми иглами, свисающими каскадом с дерева. Живёт на старых дубах и буках. Съедобен и ценится как деликатес. Исчезает вместе со старовозрастными широколиственными лесами.',
|
||||
fact: 'Гриб-ёж содержит вещества, стимулирующие рост нейронов. Изучается как потенциальное средство при болезни Альцгеймера.',
|
||||
threats: ['Вырубка старых деревьев', 'Сбор грибниками'],
|
||||
conservation: 'Сохранение старых дубрав. Запрет сбора.',
|
||||
where_to_see: 'Беловежская пуща',
|
||||
biomass: 0.5, seasons: ['8','9','10'],
|
||||
regions: ['brest'],
|
||||
});
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
ОБНОВИТЬ СЕЗОННОСТЬ СУЩЕСТВУЮЩИХ ВИДОВ
|
||||
══════════════════════════════════════════════════════════════════════════ */
|
||||
const seasonUpdate = db.prepare("UPDATE rb_species SET season_active = ? WHERE name_ru = ?");
|
||||
const seasonMap = [
|
||||
['Орлан-белохвост', JSON.stringify(['1','2','3','4','5','6','7','8','9','10','11','12'])],
|
||||
['Чёрный аист', JSON.stringify(['4','5','6','7','8','9'])],
|
||||
['Скопа', JSON.stringify(['4','5','6','7','8','9'])],
|
||||
['Коростель', JSON.stringify(['5','6','7','8'])],
|
||||
['Серый журавль', JSON.stringify(['3','4','5','6','7','8','9','10'])],
|
||||
['Филин', JSON.stringify(['1','2','3','4','5','6','7','8','9','10','11','12'])],
|
||||
['Зубр', JSON.stringify(['1','2','3','4','5','6','7','8','9','10','11','12'])],
|
||||
['Рысь', JSON.stringify(['1','2','3','4','5','6','7','8','9','10','11','12'])],
|
||||
['Бурый медведь', JSON.stringify(['4','5','6','7','8','9','10'])],
|
||||
['Бобёр европейский', JSON.stringify(['1','2','3','4','5','6','7','8','9','10','11','12'])],
|
||||
['Выдра речная', JSON.stringify(['1','2','3','4','5','6','7','8','9','10','11','12'])],
|
||||
['Стерлядь', JSON.stringify(['4','5','6','7','8','9'])],
|
||||
['Болотная черепаха', JSON.stringify(['4','5','6','7','8','9'])],
|
||||
['Венерин башмачок', JSON.stringify(['5','6'])],
|
||||
['Водяной орех', JSON.stringify(['6','7','8','9'])],
|
||||
['Жук-олень', JSON.stringify(['6','7','8'])],
|
||||
['Махаон', JSON.stringify(['5','6','7','8','9'])],
|
||||
['Богомол обыкновенный', JSON.stringify(['7','8','9'])],
|
||||
['Трюфель белый', JSON.stringify(['8','9','10','11'])],
|
||||
];
|
||||
seasonMap.forEach(([name, json]) => seasonUpdate.run(json, name));
|
||||
|
||||
db.exec('COMMIT');
|
||||
console.log('✓ Дополнительные виды добавлены');
|
||||
console.log(` Всего видов: ${db.prepare('SELECT COUNT(*) as n FROM rb_species').get().n}`);
|
||||
console.log(` Пищевых связей: ${db.prepare('SELECT COUNT(*) as n FROM rb_food_web').get().n}`);
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
ПИЩЕВЫЕ СВЯЗИ ДЛЯ НОВЫХ ВИДОВ
|
||||
══════════════════════════════════════════════════════════════════════════ */
|
||||
db.exec('BEGIN');
|
||||
|
||||
const newLinks = [
|
||||
// Лебедь-шипун — водные растения
|
||||
[lebed, getId('Водяной орех'), 0.4],
|
||||
// Лебедь-кликун — водные растения
|
||||
[klikun, getId('Водяной орех'), 0.4],
|
||||
// Сом — рыба, лягушки
|
||||
[som, getId('Стерлядь'), 0.3],
|
||||
[som, getId('Хариус европейский'), 0.3],
|
||||
// Гадюка — грызуны (опосредованно через тритона)
|
||||
[gadyuka, getId('Тритон обыкновенный') || listvy_trit, 0.4],
|
||||
// Гребенчатый тритон — насекомые
|
||||
[listvy_trit, getId('Богомол обыкновенный'), 0.1],
|
||||
// Выдра охотится на хариуса и сома
|
||||
[getId('Выдра речная'), som, 0.2],
|
||||
[getId('Выдра речная'), taimen, 0.3],
|
||||
// Орлан на лебедей (редко)
|
||||
[getId('Орлан-белохвост'), lebed, 0.1],
|
||||
// Рысь — зайца
|
||||
[getId('Рысь'), zaets, 0.4],
|
||||
// Волк — зайца
|
||||
[getId('Волк обыкновенный'), zaets, 0.3],
|
||||
// Ласка — полёвки (через вид)
|
||||
[lasitsa, getId('Коростель'), 0.1],
|
||||
// Пухляк и семена
|
||||
[getId('Воробьиный сыч'), pukhlyak, 0.2],
|
||||
// Широкоушка — насекомые
|
||||
[night_bat, getId('Махаон'), 0.2],
|
||||
[night_bat, getId('Богомол обыкновенный'), 0.2],
|
||||
].filter(([p, q]) => p && q);
|
||||
|
||||
newLinks.forEach(([p, q, s]) => {
|
||||
try { insWeb.run(p, q, s); } catch {}
|
||||
});
|
||||
db.exec('COMMIT');
|
||||
console.log(` Новых пищевых связей: ${newLinks.length}`);
|
||||
console.log('\n✅ seed-red-book-extra.js завершён!');
|
||||
@@ -0,0 +1,493 @@
|
||||
/**
|
||||
* seed-red-book-phase2.js
|
||||
* +20 видов: мхи/лишайники, пресноводные, редкие насекомые, птицы болот, ночные хищники
|
||||
* +10 квестов
|
||||
* Запуск: node src/db/seed-red-book-phase2.js (из папки backend/)
|
||||
* Идемпотентен — не дублирует существующее.
|
||||
*/
|
||||
require('./migrate');
|
||||
const db = require('./db');
|
||||
|
||||
/* ── helpers ── */
|
||||
const groupRow = n => db.prepare('SELECT id FROM rb_groups WHERE name_ru = ?').get(n);
|
||||
const habitatRow = t => db.prepare('SELECT id FROM rb_habitats WHERE type = ?').get(t);
|
||||
const getId = n => db.prepare('SELECT id FROM rb_species WHERE name_ru = ?').get(n)?.id || null;
|
||||
|
||||
const insSp = db.prepare(`
|
||||
INSERT INTO rb_species
|
||||
(group_id, habitat_id, name_ru, name_be, name_lat, category, by_category,
|
||||
description, interesting_fact, threats, conservation, where_to_see,
|
||||
photo_url, model_type, population_trend, biomass_kg, season_active)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
`);
|
||||
const insReg = db.prepare('INSERT OR IGNORE INTO rb_species_regions (species_id, region_code) VALUES (?,?)');
|
||||
const insWeb = db.prepare('INSERT OR IGNORE INTO rb_food_web (predator_id, prey_id, strength) VALUES (?,?,?)');
|
||||
|
||||
function addSp(d) {
|
||||
const existing = db.prepare('SELECT id FROM rb_species WHERE name_ru = ?').get(d.name_ru);
|
||||
if (existing) return existing.id;
|
||||
const gid = groupRow(d.group)?.id;
|
||||
const hid = habitatRow(d.habitat)?.id || null;
|
||||
if (!gid) { console.warn('Group not found:', d.group); return null; }
|
||||
const id = insSp.run(
|
||||
gid, hid,
|
||||
d.name_ru, d.name_be || '', d.name_lat || '',
|
||||
d.category, d.by_category || 'III',
|
||||
d.description || '', d.fact || '',
|
||||
JSON.stringify(d.threats || []),
|
||||
d.conservation || '', d.where_to_see || '',
|
||||
d.photo || '', d.model || 'silhouette',
|
||||
JSON.stringify(d.trend || []),
|
||||
d.biomass || 0,
|
||||
JSON.stringify(d.seasons || []),
|
||||
).lastInsertRowid;
|
||||
(d.regions || []).forEach(r => insReg.run(id, r));
|
||||
return id;
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════════════
|
||||
БЛОК 1 — ВИДЫ
|
||||
════════════════════════════════════════════════════════════════════════ */
|
||||
db.exec('BEGIN');
|
||||
|
||||
/* ── ПТИЦЫ (3) ─────────────────────────────────────────────────────── */
|
||||
|
||||
const kamyshevka = addSp({
|
||||
group: 'Птицы', habitat: 'wetland',
|
||||
name_ru: 'Вертлявая камышевка', name_be: 'Вярцёжная чаротнiца', name_lat: 'Acrocephalus paludicola',
|
||||
category: 'CR', by_category: 'I',
|
||||
description: 'Одна из наиболее угрожаемых перелётных птиц Европы. Гнездится исключительно в низинных болотах с осоково-злаковой растительностью. Беларусь — мировой центр гнездования, здесь обитает до 80 % мировой популяции. Зимует в западной Африке.',
|
||||
fact: 'Беларусь является главным «домом» вертлявой камышевки — здесь гнездится большинство всех особей планеты.',
|
||||
threats: ['Осушение болот', 'Зарастание гнездовых угодий', 'Пожары в поймах', 'Потеря зимовок в Африке'],
|
||||
conservation: 'Охрана низинных болот. Восстановление обводнённых пойм. Мониторинг популяции.',
|
||||
where_to_see: 'Споровское болото (Брестская обл.), Ельня (Витебская обл.), Дикое болото',
|
||||
biomass: 0.013, seasons: ['5','6','7','8'],
|
||||
trend: [{year:1990,count_estimate:8000,source:'IUCN'},{year:2000,count_estimate:4500,source:'BirdLife'},{year:2010,count_estimate:2800,source:'BirdLife'},{year:2024,count_estimate:2100,source:'АПБ'}],
|
||||
regions: ['vitebsk','grodno','brest','minsk'],
|
||||
});
|
||||
|
||||
const dupen = addSp({
|
||||
group: 'Птицы', habitat: 'wetland',
|
||||
name_ru: 'Дупель', name_be: 'Дупель', name_lat: 'Gallinago media',
|
||||
category: 'EN', by_category: 'II',
|
||||
description: 'Скрытный кулик заливных лугов и болот. Самцы собираются на токах, где демонстрируют оперение и трясутся всем телом. После спаривания самка одна воспитывает птенцов. Ночной образ жизни затрудняет учёт.',
|
||||
fact: 'Самцы дупеля токуют по ночам на «арене» — постоянном месте сбора, которое используется десятилетиями.',
|
||||
threats: ['Осушение пойменных лугов', 'Раннее сенокошение', 'Браконьерство на пролёте'],
|
||||
conservation: 'Запрет сенокошения до августа на ключевых участках. Сохранение влажных лугов.',
|
||||
where_to_see: 'Ельня, Освейское озеро, пойма р. Припять',
|
||||
biomass: 0.18, seasons: ['4','5','6','7','8','9'],
|
||||
trend: [{year:1995,count_estimate:1200,source:'НООО БПО'},{year:2010,count_estimate:650,source:'НООО БПО'},{year:2024,count_estimate:400,source:'НООО БПО'}],
|
||||
regions: ['vitebsk','minsk','grodno','brest'],
|
||||
});
|
||||
|
||||
const kobchik = addSp({
|
||||
group: 'Птицы', habitat: 'meadow',
|
||||
name_ru: 'Кобчик', name_be: 'Кабчык', name_lat: 'Falco vespertinus',
|
||||
category: 'VU', by_category: 'III',
|
||||
description: 'Изящный сокол размером с галку. Самцы — шиферно-серые с рыжей «штанишками», самки рябые. Охотится на крупных насекомых — прямокрылых, стрекоз, жуков. Гнездится колониями в чужих гнёздах — грачей и сорок. Перелётный, зимует в Африке.',
|
||||
fact: 'Кобчик охотится как ласточка — хватает насекомых прямо в воздухе на бреющем полёте над лугами.',
|
||||
threats: ['Деградация пойменных лугов', 'Применение пестицидов (уменьшение насекомых)', 'Уничтожение колоний грачей'],
|
||||
conservation: 'Сохранение лугов. Запрет уничтожения грачиных колоний.',
|
||||
where_to_see: 'Припятский НП, луга Брестской и Гомельской областей',
|
||||
biomass: 0.16, seasons: ['5','6','7','8','9'],
|
||||
trend: [{year:1990,count_estimate:900,source:'БО'},{year:2005,count_estimate:550,source:'БО'},{year:2024,count_estimate:320,source:'АПБ'}],
|
||||
regions: ['brest','gomel','minsk','vitebsk'],
|
||||
});
|
||||
|
||||
/* ── МЛЕКОПИТАЮЩИЕ (2) ──────────────────────────────────────────────── */
|
||||
|
||||
const vyhuhol = addSp({
|
||||
group: 'Млекопитающие', habitat: 'river',
|
||||
name_ru: 'Выхухоль русская', name_be: 'Расамаха расійская', name_lat: 'Desmana moschata',
|
||||
category: 'CR', by_category: 'I',
|
||||
description: 'Один из древнейших зверьков Земли — живой ископаемый. Обитает в старицах, заводях и медленных протоках рек Припятского бассейна. Почти слепа, ориентируется по обонянию и осязанию через вибриссы на хоботке. Выделяет мускусный секрет. Под угрозой исчезновения в Беларуси.',
|
||||
fact: 'Выхухоль существует без изменений более 30 миллионов лет — её предки жили рядом с носорогами и мамонтами.',
|
||||
threats: ['Загрязнение рек', 'Осушение пойм', 'Рыболовные сети', 'Хищники (норка американская)'],
|
||||
conservation: 'Охрана пойменных угодий р. Припять. Борьба с инвазивной норкой американской. Заповедники.',
|
||||
where_to_see: 'Пойма р. Припять (Гомельская и Брестская обл.), Припятский НП',
|
||||
biomass: 0.42, seasons: ['1','2','3','4','5','6','7','8','9','10','11','12'],
|
||||
trend: [{year:1990,count_estimate:1500,source:'НАН'},{year:2000,count_estimate:700,source:'НАН'},{year:2010,count_estimate:280,source:'НАН'},{year:2024,count_estimate:80,source:'IUCN'}],
|
||||
regions: ['gomel','brest'],
|
||||
});
|
||||
|
||||
const nochnitza = addSp({
|
||||
group: 'Млекопитающие', habitat: 'river',
|
||||
name_ru: 'Ночница прудовая', name_be: 'Начніца сажалкавая', name_lat: 'Myotis dasycneme',
|
||||
category: 'EN', by_category: 'II',
|
||||
description: 'Крупная летучая мышь, охотящаяся над открытой водой. Подхватывает насекомых с поверхности прудов и рек с помощью крыльев или хвостовой перепонки. Зимует в подземельях. Образует небольшие колонии в постройках. Чувствительна к беспокойству зимовок.',
|
||||
fact: 'Ночница прудовая — настоящий рыбак: она «читает» рябь воды ушами, определяя местонахождение добычи под поверхностью.',
|
||||
threats: ['Уничтожение зимовок', 'Загрязнение водоёмов', 'Ветроэнергетика', 'Применение пестицидов'],
|
||||
conservation: 'Охрана зимовок (подземелья, форты). Мониторинг колоний. Сохранение прибрежных водоёмов.',
|
||||
where_to_see: 'Гродненские подземелья, водохранилища Минской обл., Беловежская пуща',
|
||||
biomass: 0.016, seasons: ['5','6','7','8','9'],
|
||||
trend: [{year:2000,count_estimate:600,source:'Bat Conservation'},{year:2015,count_estimate:350,source:'БО'},{year:2024,count_estimate:220,source:'БО'}],
|
||||
regions: ['minsk','grodno','brest','vitebsk'],
|
||||
});
|
||||
|
||||
/* ── РАСТЕНИЯ (2) ───────────────────────────────────────────────────── */
|
||||
|
||||
const yatrish = addSp({
|
||||
group: 'Растения', habitat: 'meadow',
|
||||
name_ru: 'Ятрышник шлемоносный', name_be: 'Ятрышнік шлемавідны', name_lat: 'Orchis militaris',
|
||||
category: 'VU', by_category: 'III',
|
||||
description: 'Редкая наземная орхидея с необычными лилово-розовыми соцветиями, лепестки которых складываются в форму человечка с «руками» и «ногами». Растёт на известняковых лугах и в светлых лесах. Требует симбиоза с почвенными грибами для прорастания семян.',
|
||||
fact: 'Семена ятрышника невесомы — в 1 грамме до 50 000 штук. Но без почвенного гриба-симбионта ни одно не прорастёт.',
|
||||
threats: ['Распашка и застройка лугов', 'Выпас скота', 'Сбор растений'],
|
||||
conservation: 'Охрана мест обитания. Запрет сбора. Создание питомников.',
|
||||
where_to_see: 'Мозырские гряды, Новогрудская возвышенность, меловые склоны р. Сож',
|
||||
biomass: 0.04, seasons: ['5','6'],
|
||||
trend: [{year:1990,count_estimate:3200,source:'НАН'},{year:2010,count_estimate:1800,source:'НАН'},{year:2024,count_estimate:950,source:'НАН'}],
|
||||
regions: ['brest','grodno','minsk','gomel'],
|
||||
});
|
||||
|
||||
const rosynka = addSp({
|
||||
group: 'Растения', habitat: 'wetland',
|
||||
name_ru: 'Росянка английская', name_be: 'Расіца англійская', name_lat: 'Drosera anglica',
|
||||
category: 'EN', by_category: 'II',
|
||||
description: 'Насекомоядное растение сфагновых болот. Длинные листья-ловушки с липкими железистыми ворсинками ловят и переваривают мух и комаров. Получает азот и фосфор из добычи, компенсируя бедность болотной почвы. Крупнейшая из белорусских росянок.',
|
||||
fact: 'Росянка английская переваривает насекомых за 24–48 часов, выделяя ферменты, аналогичные желудочному соку животных.',
|
||||
threats: ['Осушение верховых болот', 'Торфоразработка', 'Изменение гидрологии'],
|
||||
conservation: 'Сохранение верховых сфагновых болот. Контроль торфоразработок.',
|
||||
where_to_see: 'Ельня, Обстерно, Мох Великий (Витебская обл.)',
|
||||
biomass: 0.008, seasons: ['6','7','8'],
|
||||
trend: [{year:1990,count_estimate:18000,source:'НАН'},{year:2010,count_estimate:9000,source:'НАН'},{year:2024,count_estimate:5200,source:'НАН'}],
|
||||
regions: ['vitebsk','grodno','minsk'],
|
||||
});
|
||||
|
||||
/* ── НАСЕКОМЫЕ (4) ──────────────────────────────────────────────────── */
|
||||
|
||||
const zhuk_olen = addSp({
|
||||
group: 'Насекомые', habitat: 'forest',
|
||||
name_ru: 'Жук-олень', name_be: 'Жук-алень', name_lat: 'Lucanus cervus',
|
||||
category: 'EN', by_category: 'II',
|
||||
description: 'Крупнейший жук Европы: самцы достигают 8 см. «Рога» — гипертрофированные верхние челюсти-мандибулы. Используются в турнирах за самку. Личинка живёт 5–8 лет в гнилой древесине дуба, питаясь разложившейся древесиной. Взрослый жук живёт всего 3–4 недели.',
|
||||
fact: 'Самец жука-оленя в рыцарском поединке захватывает соперника рогами и сбрасывает с ветки — как настоящий турнир средневековых рыцарей.',
|
||||
threats: ['Вырубка старых дубрав', 'Удаление пней и гниющей древесины', 'Уличное освещение (дезориентирует)'],
|
||||
conservation: 'Сохранение старых дубрав и пней. Создание «мёртвой древесины» в парках.',
|
||||
where_to_see: 'Беловежская пуща, Налибокская пуща, Гродненская пуща',
|
||||
biomass: 0.006, seasons: ['6','7','8'],
|
||||
trend: [{year:1990,count_estimate:12000,source:'НАН'},{year:2010,count_estimate:5500,source:'НАН'},{year:2024,count_estimate:2800,source:'НАН'}],
|
||||
regions: ['brest','grodno','minsk'],
|
||||
});
|
||||
|
||||
const dozorshik = addSp({
|
||||
group: 'Насекомые', habitat: 'wetland',
|
||||
name_ru: 'Дозорщик-повелитель', name_be: 'Дазорца-ўладар', name_lat: 'Anax imperator',
|
||||
category: 'VU', by_category: 'III',
|
||||
description: 'Крупнейшая стрекоза Беларуси с размахом крыльев до 10 см. Самцы патрулируют водоёмы, агрессивно охраняя территорию. Охотится на других стрекоз, бабочек и даже небольших рыб. Личинка — активный хищник, живущий в воде 2–3 года.',
|
||||
fact: 'Дозорщик-повелитель преследует и ловит добычу с точностью до 97% — лучший результат среди всех хищников Земли.',
|
||||
threats: ['Эвтрофикация водоёмов', 'Осушение болот', 'Применение пестицидов'],
|
||||
conservation: 'Охрана водно-болотных угодий. Ограничение применения инсектицидов.',
|
||||
where_to_see: 'Нарочанский НП, Споровское болото, пруды Брестской обл.',
|
||||
biomass: 0.0009, seasons: ['6','7','8','9'],
|
||||
trend: [{year:2000,count_estimate:5000,source:'ЭО'},{year:2015,count_estimate:2800,source:'ЭО'},{year:2024,count_estimate:1600,source:'ЭО'}],
|
||||
regions: ['brest','gomel','grodno','minsk'],
|
||||
});
|
||||
|
||||
const shmel = addSp({
|
||||
group: 'Насекомые', habitat: 'meadow',
|
||||
name_ru: 'Шмель моховой', name_be: 'Шмель імшысты', name_lat: 'Bombus muscorum',
|
||||
category: 'EN', by_category: 'II',
|
||||
description: 'Рыжевато-жёлтый шмель сырых лугов и болот. Один из немногих видов шмелей, гнездящихся на поверхности — в старых птичьих гнёздах или кустах мха. Важнейший опылитель растений заболоченных угодий. Страдает от потери местообитаний сильнее других шмелей.',
|
||||
fact: 'Шмель моховой — «горячая машина»: перед полётом в холодный день он вибрирует мышцами, разогревая тело до +35°C при температуре воздуха около 0°C.',
|
||||
threats: ['Интенсификация сельского хозяйства', 'Осушение лугов', 'Применение пестицидов', 'Болезни (Nosema)'],
|
||||
conservation: 'Сохранение разнотравных сырых лугов. Запрет инсектицидов на природных территориях.',
|
||||
where_to_see: 'Пойменные луга Полесья, заказник «Ельня», Витебские озёра',
|
||||
biomass: 0.0004, seasons: ['4','5','6','7','8','9'],
|
||||
trend: [{year:1990,count_estimate:50000,source:'ЭО'},{year:2010,count_estimate:18000,source:'ЭО'},{year:2024,count_estimate:7000,source:'ЭО'}],
|
||||
regions: ['vitebsk','minsk','grodno','brest'],
|
||||
});
|
||||
|
||||
const krasotEl = addSp({
|
||||
group: 'Насекомые', habitat: 'forest',
|
||||
name_ru: 'Красотел пахучий', name_be: 'Прыгажун духмяны', name_lat: 'Calosoma sycophanta',
|
||||
category: 'VU', by_category: 'III',
|
||||
description: 'Охотник за гусеницами: жук поднимается на деревья в погоне за добычей и может съесть до 400 гусениц за сезон. Окраска переливается всеми цветами радуги — синим, зелёным, золотистым. При угрозе выбрызгивает едкую жидкость с резким запахом.',
|
||||
fact: 'Красотел пахучий — сознательный лесник: он специально охотится на видах-вредителях (шелкопряд, непарный шелкопряд), защищая лес от объедания.',
|
||||
threats: ['Применение инсектицидов', 'Вырубка широколиственных лесов', 'Сбор коллекционерами'],
|
||||
conservation: 'Отказ от химических методов борьбы с вредителями. Охрана широколиственных лесов.',
|
||||
where_to_see: 'Беловежская пуща, Налибокская пуща, широколиственные леса Гродненщины',
|
||||
biomass: 0.003, seasons: ['5','6','7','8'],
|
||||
trend: [{year:1990,count_estimate:8000,source:'НАН'},{year:2010,count_estimate:3500,source:'НАН'},{year:2024,count_estimate:1800,source:'НАН'}],
|
||||
regions: ['brest','grodno','minsk','vitebsk'],
|
||||
});
|
||||
|
||||
/* ── РЫБЫ (3) ───────────────────────────────────────────────────────── */
|
||||
|
||||
const forel = addSp({
|
||||
group: 'Рыбы', habitat: 'river',
|
||||
name_ru: 'Форель ручьевая', name_be: 'Стронга ручайная', name_lat: 'Salmo trutta fario',
|
||||
category: 'VU', by_category: 'III',
|
||||
description: 'Пёстрая хищная рыба холодных прозрачных ручьёв и малых рек. Индикатор чистоты воды: погибает при малейшем загрязнении. Требует хорошую аэрацию и чистый гравийный субстрат для нереста. В Беларуси — на южной границе ареала, обитает только в реках Нарочанского бассейна.',
|
||||
fact: 'Форель настолько требовательна к кислороду, что может жить только там, где вода кристально чистая. Там, где есть форель — вода питьевого качества.',
|
||||
threats: ['Загрязнение рек', 'Нагрев воды', 'Браконьерство', 'Мелиорация'],
|
||||
conservation: 'Охрана малых рек. Контроль качества воды. Ограничение рыбалки. Искусственное разведение.',
|
||||
where_to_see: 'Реки Нарочь, Страча, Сервечь (Минская обл.), р. Щара (Гродненская обл.)',
|
||||
biomass: 0.35, seasons: ['1','2','3','4','5','6','7','8','9','10','11','12'],
|
||||
trend: [{year:1990,count_estimate:4200,source:'ВТ'},{year:2005,count_estimate:2100,source:'ВТ'},{year:2024,count_estimate:900,source:'ВТ'}],
|
||||
regions: ['vitebsk','grodno','minsk'],
|
||||
});
|
||||
|
||||
const bystryanka = addSp({
|
||||
group: 'Рыбы', habitat: 'river',
|
||||
name_ru: 'Быстрянка обыкновенная', name_be: 'Быстранка звычайная', name_lat: 'Alburnoides bipunctatus',
|
||||
category: 'EN', by_category: 'II',
|
||||
description: 'Маленькая серебристая рыбка быстрых чистых рек с выраженным течением. Держится у поверхности стайками, питаясь насекомыми. Чувствительна к загрязнению воды. Один из исчезающих видов пресноводных рыб Беларуси, встречается единично в реках западной части страны.',
|
||||
fact: 'Быстрянка — «наземная» рыба: до 80% её рациона составляют упавшие в воду насекомые — комары, мухи, падёнки.',
|
||||
threats: ['Загрязнение рек', 'Зарегулирование стока', 'Конкуренция с инвазивными видами'],
|
||||
conservation: 'Охрана малых рек с быстрым течением. Мониторинг популяции.',
|
||||
where_to_see: 'Реки Зельвянка, Россь, Нёман (Гродненская обл.)',
|
||||
biomass: 0.018, seasons: ['4','5','6','7','8','9','10'],
|
||||
trend: [{year:1990,count_estimate:15000,source:'НАН'},{year:2010,count_estimate:5000,source:'НАН'},{year:2024,count_estimate:1200,source:'НАН'}],
|
||||
regions: ['grodno','brest','minsk'],
|
||||
});
|
||||
|
||||
const umbra = addSp({
|
||||
group: 'Рыбы', habitat: 'wetland',
|
||||
name_ru: 'Умбра европейская', name_be: 'Умбра еўрапейская', name_lat: 'Umbra krameri',
|
||||
category: 'CR', by_category: 'I',
|
||||
description: 'Реликтовая рыбка — живой свидетель ледникового периода. Обитает только в медленно текущих водотоках и болотных канавах Полесья. Может переносить высыхание, зарываясь в ил. Дышит атмосферным воздухом при нехватке кислорода. Крупнейшая популяция в Европе — в Припятском полесье.',
|
||||
fact: 'Умбра — одна из немногих рыб, которая пережила все ледниковые периоды, прячась в рефугиях болот. Ей более 5 миллионов лет.',
|
||||
threats: ['Осушение болот', 'Углубление канав', 'Загрязнение воды', 'Вселение хищных рыб'],
|
||||
conservation: 'Охрана болотных водотоков Полесья. Мониторинг популяции. Разведение в неволе.',
|
||||
where_to_see: 'Припятский НП, болота Столинского района (Брестская обл.), пойма Ствиги',
|
||||
biomass: 0.025, seasons: ['1','2','3','4','5','6','7','8','9','10','11','12'],
|
||||
trend: [{year:1990,count_estimate:3500,source:'НАН'},{year:2005,count_estimate:1200,source:'НАН'},{year:2024,count_estimate:300,source:'IUCN'}],
|
||||
regions: ['brest','gomel'],
|
||||
});
|
||||
|
||||
/* ── ГРИБЫ (2) ──────────────────────────────────────────────────────── */
|
||||
|
||||
const felhodon = addSp({
|
||||
group: 'Грибы', habitat: 'conifer',
|
||||
name_ru: 'Феллодон слитый', name_be: 'Фелодон злiты', name_lat: 'Phellodon confluens',
|
||||
category: 'EN', by_category: 'II',
|
||||
description: 'Редкий коралловидный гриб хвойных и смешанных лесов. Несколько плодовых тел часто сливаются в одну неправильную форму, откуда и название. Серовато-белый, с тонкими иглами на нижней поверхности шляпки. Индикатор старых ненарушенных лесов. Занесён в Красные книги 12 стран.',
|
||||
fact: 'Феллодон слитый растёт только в лесах, которым не менее 150 лет — он служит биологическим маркером древних экосистем.',
|
||||
threats: ['Рубки главного пользования', 'Нарушение лесной подстилки', 'Рекреационная нагрузка'],
|
||||
conservation: 'Сохранение старовозрастных лесов. Организация микозаповедников.',
|
||||
where_to_see: 'Беловежская пуща, Налибокская пуща, хвойные леса Витебской обл.',
|
||||
biomass: 0.06, seasons: ['8','9','10'],
|
||||
trend: [{year:1990,count_estimate:200,source:'НАН'},{year:2010,count_estimate:85,source:'НАН'},{year:2024,count_estimate:40,source:'НАН'}],
|
||||
regions: ['vitebsk','minsk','grodno','brest'],
|
||||
});
|
||||
|
||||
const gidnellum = addSp({
|
||||
group: 'Грибы', habitat: 'forest',
|
||||
name_ru: 'Гиднеллум синеющий', name_be: 'Гiднелум сiнеючы', name_lat: 'Hydnellum caeruleum',
|
||||
category: 'VU', by_category: 'III',
|
||||
description: 'Необычный ежовый гриб с сине-фиолетовым или голубовато-серым молодым плодовым телом, темнеющим до тёмно-коричневого. Растёт в мшистых хвойных лесах, образуя микоризу с соснами и елями. Очень горький на вкус, несъедобен. Исчезает при нарушении лесной экосистемы.',
|
||||
fact: 'Гиднеллум синеющий настолько редок, что находка даже одного плодового тела считается значимым событием для микологов всей Европы.',
|
||||
threats: ['Лесозаготовки', 'Удаление мохового покрова', 'Загрязнение атмосферного воздуха'],
|
||||
conservation: 'Выявление и охрана местонахождений. Сохранение мшистых ельников.',
|
||||
where_to_see: 'Налибокская пуща, Борисовско-Березинский резерват',
|
||||
biomass: 0.04, seasons: ['8','9','10'],
|
||||
trend: [{year:2000,count_estimate:60,source:'НАН'},{year:2015,count_estimate:28,source:'НАН'},{year:2024,count_estimate:15,source:'НАН'}],
|
||||
regions: ['vitebsk','minsk','grodno'],
|
||||
});
|
||||
|
||||
/* ── МХИ И ЛИШАЙНИКИ (4) ────────────────────────────────────────────── */
|
||||
|
||||
const buksbaumiya = addSp({
|
||||
group: 'Мхи и лишайники', habitat: 'conifer',
|
||||
name_ru: 'Буксбаумия безлистная', name_be: 'Буксбаумiя бязлістая', name_lat: 'Buxbaumia aphylla',
|
||||
category: 'EN', by_category: 'II',
|
||||
description: 'Один из самых необычных мхов: почти без листьев, с единственным крупным непропорциональным спорангием на тонкой ножке. Споровая коробочка напоминает панцирь черепахи. Растёт на гниющих пнях хвойных деревьев. Исчезает вместе с исчезновением гниющей древесины в заготовительных лесах.',
|
||||
fact: 'Буксбаумия безлистная проводит почти всю жизнь как невидимая нить микоризы — листовая часть появляется лишь на несколько недель для спороношения.',
|
||||
threats: ['Уборка гнилой древесины', 'Интенсивные рубки', 'Нарушение лесной подстилки'],
|
||||
conservation: 'Сохранение пней и гниющей древесины в лесах. Организация «мёртвого дерева» в заказниках.',
|
||||
where_to_see: 'Ельники Витебской и Минской обл., Беловежская пуща',
|
||||
biomass: 0.001, seasons: ['9','10','11','3','4'],
|
||||
trend: [{year:1990,count_estimate:800,source:'НАН'},{year:2010,count_estimate:320,source:'НАН'},{year:2024,count_estimate:150,source:'НАН'}],
|
||||
regions: ['vitebsk','minsk','grodno','brest'],
|
||||
});
|
||||
|
||||
const meesiya = addSp({
|
||||
group: 'Мхи и лишайники', habitat: 'wetland',
|
||||
name_ru: 'Меезия трёхгранная', name_be: 'Меезiя трохгранная', name_lat: 'Meesia triquetra',
|
||||
category: 'VU', by_category: 'III',
|
||||
description: 'Болотный мох с характерными трёхгранными листьями, образующий плотные дернины в основаниях сфагновых кочек. Вид-индикатор ненарушенных верховых болот, чувствителен к изменению гидрологического режима. В Беларуси находится на южной границе ареала и встречается крайне редко.',
|
||||
fact: 'Меезия трёхгранная может фиксировать свидетельства климата за последние тысячи лет — учёные читают историю болота по слоям этого мха как по книге.',
|
||||
threats: ['Осушение болот', 'Добыча торфа', 'Изменение уровня грунтовых вод'],
|
||||
conservation: 'Охрана верховых болот. Поддержание высокого уровня воды в болотных комплексах.',
|
||||
where_to_see: 'Ельня (Витебская обл.), Мох Великий, Освейское болото',
|
||||
biomass: 0.002, seasons: ['1','2','3','4','5','6','7','8','9','10','11','12'],
|
||||
trend: [{year:1990,count_estimate:500,source:'НАН'},{year:2010,count_estimate:200,source:'НАН'},{year:2024,count_estimate:90,source:'НАН'}],
|
||||
regions: ['vitebsk','minsk'],
|
||||
});
|
||||
|
||||
const usnea = addSp({
|
||||
group: 'Мхи и лишайники', habitat: 'conifer',
|
||||
name_ru: 'Уснея длиннейшая', name_be: 'Уснея найдаўжэйшая', name_lat: 'Usnea longissima',
|
||||
category: 'CR', by_category: 'I',
|
||||
description: 'Бородатый лишайник, свисающий с ветвей хвойных деревьев нитями длиной до 3 метров. Требует исключительно чистого воздуха и высокой влажности. Был широко распространён в старых лесах Беларуси, но исчез почти повсеместно из-за загрязнения воздуха и вырубок. Сегодня — в единичных местонахождениях.',
|
||||
fact: 'Уснея длиннейшая растёт со скоростью 1–2 мм в год, а её трёхметровые нити означают столетие жизни. Найти её — значит найти вековой нетронутый лес.',
|
||||
threats: ['Загрязнение воздуха', 'Вырубка старых хвойных лесов', 'Изменение климата'],
|
||||
conservation: 'Строгая охрана мест произрастания. Мониторинг качества воздуха. Охрана старолесий.',
|
||||
where_to_see: 'Налибокская пуща, отдельные участки Витебской обл.',
|
||||
biomass: 0.012, seasons: ['1','2','3','4','5','6','7','8','9','10','11','12'],
|
||||
trend: [{year:1990,count_estimate:120,source:'НАН'},{year:2005,count_estimate:35,source:'НАН'},{year:2024,count_estimate:8,source:'НАН'}],
|
||||
regions: ['vitebsk','grodno'],
|
||||
});
|
||||
|
||||
const peltigera = addSp({
|
||||
group: 'Мхи и лишайники', habitat: 'forest',
|
||||
name_ru: 'Пельтигера горизонтальная', name_be: 'Пельцiгера гарызантальная', name_lat: 'Peltigera horizontalis',
|
||||
category: 'VU', by_category: 'III',
|
||||
description: 'Крупный листоватый лишайник с горизонтально распластанным слоевищем, вырастающим до 20 см в диаметре. Серо-голубой сверху, с заметными нитями гиф и цианобактерий на нижней поверхности. Растёт на замшелых стволах и основаниях деревьев в старых широколиственных лесах. Фиксирует атмосферный азот.',
|
||||
fact: 'Пельтигера — симбиоз гриба, водорослей и цианобактерий в одном организме. Она единственная из лишайников способна удобрять почву, фиксируя воздушный азот.',
|
||||
threats: ['Вырубка широколиственных лесов', 'Рекреационное вытаптывание', 'Загрязнение воздуха'],
|
||||
conservation: 'Сохранение старых широколиственных лесов. Ограничение рекреационной нагрузки.',
|
||||
where_to_see: 'Беловежская пуща, Налибокская пуща, старые широколиственные леса Гродненщины',
|
||||
biomass: 0.006, seasons: ['1','2','3','4','5','6','7','8','9','10','11','12'],
|
||||
trend: [{year:1990,count_estimate:350,source:'НАН'},{year:2010,count_estimate:140,source:'НАН'},{year:2024,count_estimate:70,source:'НАН'}],
|
||||
regions: ['vitebsk','minsk','grodno','brest'],
|
||||
});
|
||||
|
||||
db.exec('COMMIT');
|
||||
console.log('✓ Виды добавлены');
|
||||
console.log(` Всего видов: ${db.prepare('SELECT COUNT(*) as n FROM rb_species').get().n}`);
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════════════
|
||||
БЛОК 2 — ПИЩЕВЫЕ СВЯЗИ
|
||||
════════════════════════════════════════════════════════════════════════ */
|
||||
db.exec('BEGIN');
|
||||
|
||||
const links = [
|
||||
// Орлан охотится на лебедей (более активно)
|
||||
[getId('Орлан-белохвост'), getId('Лебедь-шипун'), 0.25],
|
||||
[getId('Орлан-белохвост'), getId('Лебедь-кликун'), 0.20],
|
||||
// Кобчик ест насекомых
|
||||
[kobchik, getId('Аполлон'), 0.50],
|
||||
[kobchik, getId('Богомол обыкновенный'), 0.30],
|
||||
[kobchik, shmel, 0.50],
|
||||
[kobchik, dozorshik, 0.40],
|
||||
// Вертлявая камышевка ест насекомых болот
|
||||
[kamyshevka, shmel, 0.20],
|
||||
[kamyshevka, dozorshik, 0.15],
|
||||
// Красотел ест бабочек/жуков
|
||||
[krasotEl, getId('Аполлон'), 0.60],
|
||||
[krasotEl, getId('Махаон'), 0.50],
|
||||
[krasotEl, zhuk_olen, 0.30],
|
||||
// Форель ест быстрянку и другую рыбу
|
||||
[forel, bystryanka, 0.80],
|
||||
[forel, getId('Хариус европейский'), 0.40],
|
||||
[forel, getId('Ручьевая минога'), 0.30],
|
||||
// Умбра — мелкий хищник
|
||||
[umbra, bystryanka, 0.30],
|
||||
// Крупные рыбы едят форель
|
||||
[getId('Сом обыкновенный'), forel, 0.40],
|
||||
[getId('Выдра речная'), forel, 0.60],
|
||||
[getId('Выдра речная'), bystryanka, 0.30],
|
||||
// Норка и выдра охотятся на выхухоль
|
||||
[getId('Европейская норка'), vyhuhol, 0.60],
|
||||
[getId('Выдра речная'), vyhuhol, 0.20],
|
||||
// Ночница ест насекомых над водой
|
||||
[nochnitza, shmel, 0.30],
|
||||
[nochnitza, dozorshik, 0.40],
|
||||
// Жук-олень поедает соки деревьев (связь через растения если есть)
|
||||
// Дупель и камышевка — косвенные связи через экосистему
|
||||
].filter(([p, q]) => p && q);
|
||||
|
||||
links.forEach(([p, q, s]) => {
|
||||
try { insWeb.run(p, q, s); } catch {}
|
||||
});
|
||||
|
||||
db.exec('COMMIT');
|
||||
console.log(` Пищевых связей добавлено: ${links.length}`);
|
||||
console.log(` Всего пищевых связей: ${db.prepare('SELECT COUNT(*) as n FROM rb_food_web').get().n}`);
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════════════
|
||||
БЛОК 3 — КВЕСТЫ (10 новых)
|
||||
════════════════════════════════════════════════════════════════════════ */
|
||||
db.exec('BEGIN');
|
||||
|
||||
function addQuest(title, description, speciesNames, xp, badge) {
|
||||
const existing = db.prepare('SELECT id FROM rb_quests WHERE title = ?').get(title);
|
||||
if (existing) { console.log(` Квест уже существует: ${title}`); return; }
|
||||
const ids = speciesNames.map(n => getId(n)).filter(Boolean);
|
||||
if (ids.length < speciesNames.length) {
|
||||
console.warn(` Квест "${title}": не найдены виды!`, speciesNames.filter(n => !getId(n)));
|
||||
}
|
||||
db.prepare('INSERT INTO rb_quests (title, description, species_ids, xp_reward, badge_slug) VALUES (?,?,?,?,?)').run(
|
||||
title, description, JSON.stringify(ids), xp, badge
|
||||
);
|
||||
}
|
||||
|
||||
addQuest(
|
||||
'Птицы болот',
|
||||
'Болота Беларуси — дом для редчайших птиц Европы. Найдите серого журавля, большого подорлика, большого кроншнепа, вертлявую камышевку и дупеля. Узнайте, почему без болот эти виды исчезнут с планеты.',
|
||||
['Серый журавль', 'Большой подорлик', 'Большой кроншнеп', 'Вертлявая камышевка', 'Дупель'],
|
||||
220, 'quest_bog_birds'
|
||||
);
|
||||
|
||||
addQuest(
|
||||
'Ночные хищники',
|
||||
'Пока мы спим, лес живёт своей жизнью. Исследуйте тайных обитателей ночи: воробьиного и домового сычей, широкоушку и прудовую ночницу. Узнайте, как эхолокация превращает темноту в охотничьи угодья.',
|
||||
['Воробьиный сыч', 'Сыч домовый', 'Широкоушка европейская', 'Ночница прудовая'],
|
||||
180, 'quest_night_hunters'
|
||||
);
|
||||
|
||||
addQuest(
|
||||
'Диковинки Витебщины',
|
||||
'Витебская область — северный форпост Беларуси со своими уникальными видами. Откройте краснозобую казарку, лобелию Дортмана, вертлявую камышевку, форель ручьевую и уснею длиннейшую — виды, которых больше нигде не встретить.',
|
||||
['Краснозобая казарка', 'Лобелия Дортмана', 'Вертлявая камышевка', 'Форель ручьевая', 'Уснея длиннейшая'],
|
||||
230, 'quest_vitebsk'
|
||||
);
|
||||
|
||||
addQuest(
|
||||
'Царство грибов',
|
||||
'Мир грибов гораздо больше того, что мы видим. Трюфель, решёточник, спарасис, ёж гриб, феллодон и гиднеллум — это не еда, это живая история леса. Найдите всех шестерых и узнайте, почему грибы — хранители экосистемы.',
|
||||
['Трюфель летний', 'Решёточник красный', 'Спарасис курчавый (грибная капуста)', 'Ёж гриб (герций)', 'Феллодон слитый', 'Гиднеллум синеющий'],
|
||||
300, 'quest_fungi'
|
||||
);
|
||||
|
||||
addQuest(
|
||||
'Мир мхов и лишайников',
|
||||
'Мхи и лишайники — первооткрыватели суши, они появились раньше динозавров. Найдите лобарию, кладонию, буксбаумию, меезию, уснею и пельтигеру — шесть хранителей нетронутых лесов и болот Беларуси.',
|
||||
['Лобария лёгочная', 'Кладония звёздчатая', 'Буксбаумия безлистная', 'Меезия трёхгранная', 'Уснея длиннейшая', 'Пельтигера горизонтальная'],
|
||||
280, 'quest_mosses'
|
||||
);
|
||||
|
||||
addQuest(
|
||||
'Легенды Полесья',
|
||||
'Полесье — болотный рай, один из последних в Европе. Его населяют легендарные жители: европейская норка, болотная черепаха, альдрованда, выхухоль русская и умбра европейская — реликты, пережившие ледниковые периоды.',
|
||||
['Европейская норка', 'Черепаха болотная', 'Альдрованда пузырчатая', 'Выхухоль русская', 'Умбра европейская'],
|
||||
350, 'quest_polesye'
|
||||
);
|
||||
|
||||
addQuest(
|
||||
'Хранители леса',
|
||||
'Лес держится на хищниках. Рысь, бурый медведь, жук-олень, усач альпийский и красотел пахучий — звенья одной цепи. Когда исчезает один хищник, лес начинает умирать. Узнайте, как они связаны между собой.',
|
||||
['Рысь евразийская', 'Бурый медведь', 'Жук-олень', 'Усач альпийский', 'Красотел пахучий'],
|
||||
250, 'quest_forest_guard'
|
||||
);
|
||||
|
||||
addQuest(
|
||||
'Тайны реки',
|
||||
'Белорусские реки скрывают живых ископаемых. Выдра, стерлядь, форель, быстрянка и умбра европейская — каждый вид-индикатор показывает, здорова ли река. Найдите их всех и прочтите историю наших вод.',
|
||||
['Речная выдра', 'Стерлядь', 'Форель ручьевая', 'Быстрянка обыкновенная', 'Умбра европейская'],
|
||||
220, 'quest_river2'
|
||||
);
|
||||
|
||||
addQuest(
|
||||
'Летающие над водой',
|
||||
'Вода с небесами — место встречи орлана-белохвоста, скопы, двух видов лебедей и дозорщика-повелителя. Все они связаны с водой и исчезнут вместе с ней. Откройте пятёрку хозяев белорусских озёр.',
|
||||
['Орлан-белохвост', 'Скопа', 'Лебедь-шипун', 'Лебедь-кликун', 'Дозорщик-повелитель'],
|
||||
200, 'quest_water_fliers'
|
||||
);
|
||||
|
||||
addQuest(
|
||||
'Краса болот',
|
||||
'Болотные растения — хищники, ловушки и редкие орхидеи. Пальчатокоренник, шейхцерия, альдрованда, вертлявая камышевка и росянка английская живут в мире, где каждая капля воды на счету. Исследуйте болото изнутри.',
|
||||
['Пальчатокоренник мясокрасный', 'Шейхцерия болотная', 'Альдрованда пузырчатая', 'Вертлявая камышевка', 'Росянка английская'],
|
||||
230, 'quest_bog_beauty'
|
||||
);
|
||||
|
||||
db.exec('COMMIT');
|
||||
console.log('✓ Квесты добавлены');
|
||||
console.log(` Всего квестов: ${db.prepare('SELECT COUNT(*) as n FROM rb_quests').get().n}`);
|
||||
console.log('\n✅ seed-red-book-phase2.js завершён!');
|
||||
console.log(` Видов: ${db.prepare('SELECT COUNT(*) as n FROM rb_species').get().n}`);
|
||||
console.log(` Квестов: ${db.prepare('SELECT COUNT(*) as n FROM rb_quests').get().n}`);
|
||||
console.log(` Пищевых связей: ${db.prepare('SELECT COUNT(*) as n FROM rb_food_web').get().n}`);
|
||||
@@ -0,0 +1,807 @@
|
||||
/**
|
||||
* Seed: Красная книга Республики Беларусь
|
||||
* 80 видов, 8 групп, пищевая сеть, популяционные тренды
|
||||
* Запуск: node src/db/seed-red-book.js (из папки backend/)
|
||||
*/
|
||||
require('./migrate');
|
||||
const db = require('./db');
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
GUARD — пропустить если уже засеяно
|
||||
══════════════════════════════════════════════════════════════════════════ */
|
||||
const already = db.prepare("SELECT COUNT(*) as n FROM rb_species").get();
|
||||
if (already.n > 0) {
|
||||
console.log(`Красная книга: уже засеяно ${already.n} видов, пропускаем.`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
ГРУППЫ
|
||||
══════════════════════════════════════════════════════════════════════════ */
|
||||
const insGroup = db.prepare('INSERT INTO rb_groups (name_ru, name_lat, icon, color) VALUES (?,?,?,?)');
|
||||
const groups = {};
|
||||
[
|
||||
['Птицы', 'Aves', '🦅', '#0369a1'],
|
||||
['Млекопитающие', 'Mammalia', '🦌', '#92400e'],
|
||||
['Растения', 'Plantae', '🌿', '#166534'],
|
||||
['Насекомые', 'Insecta', '🦋', '#b45309'],
|
||||
['Рыбы', 'Pisces', '🐟', '#0e7490'],
|
||||
['Рептилии и амфибии','Reptilia/Amphibia','🦎','#4d7c0f'],
|
||||
['Грибы', 'Fungi', '🍄', '#7c3aed'],
|
||||
['Мхи и лишайники', 'Bryophyta', '🌱', '#065f46'],
|
||||
].forEach(([name_ru, name_lat, icon, color]) => {
|
||||
const id = insGroup.run(name_ru, name_lat, icon, color).lastInsertRowid;
|
||||
groups[name_ru] = id;
|
||||
});
|
||||
console.log('✓ Группы');
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
БИОМЫ
|
||||
══════════════════════════════════════════════════════════════════════════ */
|
||||
const insHabitat = db.prepare('INSERT INTO rb_habitats (name, type, description, sound_file) VALUES (?,?,?,?)');
|
||||
const habitats = {};
|
||||
[
|
||||
['Широколиственный лес', 'forest', 'Дубравы и грабово-дубовые леса Полесья и центральной Беларуси', 'forest.mp3'],
|
||||
['Хвойный лес', 'conifer', 'Сосновые и еловые боры, тайга северной Беларуси', 'conifer.mp3'],
|
||||
['Болото', 'wetland', 'Верховые и низинные болота, крупнейшие в Европе', 'wetland.mp3'],
|
||||
['Река и озеро', 'river', 'Пресноводные водоёмы: Припять, Нёман, Западная Двина', 'river.mp3'],
|
||||
['Луг и поле', 'meadow', 'Заливные и суходольные луга, агроландшафты', 'meadow.mp3'],
|
||||
].forEach(([name, type, description, sound_file]) => {
|
||||
const id = insHabitat.run(name, type, description, sound_file).lastInsertRowid;
|
||||
habitats[name] = id;
|
||||
});
|
||||
console.log('✓ Биомы');
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
ВИДЫ
|
||||
══════════════════════════════════════════════════════════════════════════ */
|
||||
const insSp = db.prepare(`
|
||||
INSERT INTO rb_species
|
||||
(group_id, habitat_id, name_ru, name_be, name_lat, category, by_category,
|
||||
description, interesting_fact, threats, conservation, where_to_see,
|
||||
photo_url, model_type, population_trend, biomass_kg)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
`);
|
||||
const insReg = db.prepare('INSERT OR IGNORE INTO rb_species_regions (species_id, region_code) VALUES (?,?)');
|
||||
const insPop = db.prepare('INSERT INTO rb_population_data (species_id, year, count_estimate, source) VALUES (?,?,?,?)');
|
||||
|
||||
function sp(data) {
|
||||
const id = insSp.run(
|
||||
groups[data.group], habitats[data.habitat] || null,
|
||||
data.name_ru, data.name_be || '', data.name_lat || '',
|
||||
data.category, data.by_category || 'III',
|
||||
data.description || '', data.fact || '',
|
||||
JSON.stringify(data.threats || []),
|
||||
data.conservation || '', data.where_to_see || '',
|
||||
data.photo || '', data.model || 'silhouette',
|
||||
JSON.stringify(data.trend || []),
|
||||
data.biomass || 0
|
||||
).lastInsertRowid;
|
||||
(data.regions || []).forEach(r => insReg.run(id, r));
|
||||
(data.popdata || []).forEach(([year, count, src]) => insPop.run(id, year, count, src || 'КК РБ'));
|
||||
return id;
|
||||
}
|
||||
|
||||
const ids = {};
|
||||
|
||||
db.exec('BEGIN');
|
||||
try { // species block
|
||||
|
||||
/* ── ПТИЦЫ ─────────────────────────────────────────────────────────────── */
|
||||
|
||||
ids.orlan = sp({
|
||||
group: 'Птицы', habitat: 'Река и озеро',
|
||||
name_ru: 'Орлан-белохвост', name_be: 'Арлан-белахвост', name_lat: 'Haliaeetus albicilla',
|
||||
category: 'NT', by_category: 'III',
|
||||
description: 'Крупнейший хищник среди белорусских орлиных. Размах крыльев достигает 240 см. Гнездится у крупных водоёмов, охотится на рыбу и уток. Пара занимает одно гнездо десятилетиями, достраивая его каждый год — масса гнезда может превышать тонну.',
|
||||
fact: 'Орлан развивает скорость пикирования до 100 км/ч. Гнездо одной пары в Беловежской пуще весило 600 кг.',
|
||||
threats: ['Беспокойство у гнёзд', 'Отравление свинцом из охотничьей дроби', 'Вырубка леса у водоёмов'],
|
||||
conservation: 'Охраняется в заповедниках. Программа мониторинга гнёзд. Запрет охоты.',
|
||||
where_to_see: 'НП «Припятский», НП «Браславские озёра», Беловежская пуща',
|
||||
model: 'procedural', biomass: 5.5,
|
||||
regions: ['brest','gomel','vitebsk','grodno','minsk','mogilev'],
|
||||
popdata: [[1990,150,'КК РБ 1993'],[2000,200,'КК РБ 2004'],[2010,280,'КК РБ 2015'],[2024,350,'Мониторинг 2024']],
|
||||
trend: [{year:1990,count:150},{year:2000,count:200},{year:2010,count:280},{year:2024,count:350}],
|
||||
});
|
||||
|
||||
ids.chorny_aist = sp({
|
||||
group: 'Птицы', habitat: 'Широколиственный лес',
|
||||
name_ru: 'Чёрный аист', name_be: 'Чорны бусел', name_lat: 'Ciconia nigra',
|
||||
category: 'NT', by_category: 'III',
|
||||
description: 'В отличие от белого аиста, чёрный предпочитает глухие леса с ручьями и реками. Оперение с зелёным и пурпурным металлическим блеском. Зимует в Африке южнее Сахары. Чрезвычайно осторожен — при беспокойстве бросает гнездо.',
|
||||
fact: 'Чёрный аист преодолевает до 10 000 км во время миграции. В Беларуси гнездится около 900 пар.',
|
||||
threats: ['Вырубка старовозрастных лесов', 'Осушение болот', 'Беспокойство у гнёзд'],
|
||||
conservation: 'Охраняемые зоны вокруг гнёзд радиусом 200 м. Сохранение старовозрастных деревьев.',
|
||||
where_to_see: 'Беловежская пуща, НП «Припятский», ПЗ «Налибокский»',
|
||||
model: 'procedural', biomass: 3.0,
|
||||
regions: ['brest','gomel','grodno','minsk','vitebsk'],
|
||||
popdata: [[1990,600,'КК РБ 1993'],[2005,800,'КК РБ 2004'],[2015,850,'КК РБ 2015'],[2024,920,'Мониторинг 2024']],
|
||||
trend: [{year:1990,count:600},{year:2005,count:800},{year:2015,count:850},{year:2024,count:920}],
|
||||
});
|
||||
|
||||
ids.skopa = sp({
|
||||
group: 'Птицы', habitat: 'Река и озеро',
|
||||
name_ru: 'Скопа', name_be: 'Скапа', name_lat: 'Pandion haliaetus',
|
||||
category: 'VU', by_category: 'II',
|
||||
description: 'Уникальный хищник, питающийся исключительно рыбой. Ныряет в воду на глубину до 1 м. Пальцы покрыты шипами для удержания скользкой добычи. Строит гнёзда на вершинах сухих деревьев или опорах ЛЭП.',
|
||||
fact: 'Скопа — единственная хищная птица мира, охотящаяся только на рыбу и обитающая на всех континентах кроме Антарктиды.',
|
||||
threats: ['Деградация рыбных ресурсов', 'Беспокойство у гнёзд', 'Гибель на ЛЭП'],
|
||||
conservation: 'Установка искусственных гнездовых платформ. Охранные зоны у гнёзд.',
|
||||
where_to_see: 'Браславские озёра, НП «Нарочанский», озёра Витебской области',
|
||||
model: 'procedural', biomass: 1.8,
|
||||
regions: ['vitebsk','minsk','grodno','brest'],
|
||||
popdata: [[1990,50,'КК РБ 1993'],[2005,70,'КК РБ 2004'],[2015,90,'КК РБ 2015'],[2024,110,'Мониторинг 2024']],
|
||||
trend: [{year:1990,count:50},{year:2005,count:70},{year:2015,count:90},{year:2024,count:110}],
|
||||
});
|
||||
|
||||
ids.dergach = sp({
|
||||
group: 'Птицы', habitat: 'Луг и поле',
|
||||
name_ru: 'Коростель', name_be: 'Дзяргач', name_lat: 'Crex crex',
|
||||
category: 'VU', by_category: 'III',
|
||||
description: 'Скрытная птица влажных лугов. Слышать его легко — самец кричит «дёрг-дёрг» всю ночь, но увидеть почти невозможно. Прилетает поздно весной из Африки. Численность резко падает из-за раннего сенокоса.',
|
||||
fact: 'Коростель бежит быстрее, чем летит. Его крик слышен за 1,5 км в тихую ночь.',
|
||||
threats: ['Ранний механизированный сенокос', 'Осушение лугов', 'Хищники'],
|
||||
conservation: 'Рекомендации по срокам сенокоса (после 1 августа). Сохранение пойменных лугов.',
|
||||
where_to_see: 'Пойма Припяти, Нарочанский НП, луга Брестской области',
|
||||
biomass: 0.15,
|
||||
regions: ['brest','gomel','grodno','minsk','vitebsk','mogilev'],
|
||||
popdata: [[1990,5000,'КК РБ 1993'],[2005,3500,'КК РБ 2004'],[2015,2800,'КК РБ 2015'],[2024,2200,'Мониторинг 2024']],
|
||||
trend: [{year:1990,count:5000},{year:2005,count:3500},{year:2015,count:2800},{year:2024,count:2200}],
|
||||
});
|
||||
|
||||
ids.zhuravl = sp({
|
||||
group: 'Птицы', habitat: 'Болото',
|
||||
name_ru: 'Серый журавль', name_be: 'Шэры жураўль', name_lat: 'Grus grus',
|
||||
category: 'NT', by_category: 'IV',
|
||||
description: 'Символ белорусских болот. Пары создаются на всю жизнь. Весной и осенью собирается на полях в стаи до нескольких тысяч птиц. Токование — один из самых красивых ритуалов среди птиц: прыжки, взмахи крыльями, трубный крик.',
|
||||
fact: 'Журавль живёт до 25 лет в дикой природе. Беларусь — один из главных «журавлиных» регионов Европы с населением ~20 000 пар.',
|
||||
threats: ['Осушение болот', 'Беспокойство в период гнездования'],
|
||||
conservation: 'Сохранение болотных массивов. Охраняемые территории на токовищах.',
|
||||
where_to_see: 'НП «Припятский», Ельня, Освейское болото',
|
||||
model: 'procedural', biomass: 5.0,
|
||||
regions: ['vitebsk','minsk','gomel','brest','grodno','mogilev'],
|
||||
trend: [{year:1990,count:15000},{year:2010,count:18000},{year:2024,count:20000}],
|
||||
});
|
||||
|
||||
ids.zmeeyed = sp({
|
||||
group: 'Птицы', habitat: 'Широколиственный лес',
|
||||
name_ru: 'Змееяд', name_be: 'Змеяед', name_lat: 'Circaetus gallicus',
|
||||
category: 'EN', by_category: 'II',
|
||||
description: 'Специализированный охотник на рептилий. Питается почти исключительно змеями, в том числе гадюками. Иммунитет к яду позволяет заглатывать змею живой. Редчайший в Беларуси — гнездятся единицы пар.',
|
||||
fact: 'Змееяд способен заглотить гадюку длиной 1 м. В Беларуси гнездится не более 15–20 пар.',
|
||||
threats: ['Вырубка старых лесов', 'Снижение численности рептилий', 'Беспокойство'],
|
||||
conservation: 'Охрана известных гнёзд. Мониторинг численности.',
|
||||
where_to_see: 'ПЗ «Полесский», Беловежская пуща',
|
||||
biomass: 1.9,
|
||||
regions: ['brest','gomel'],
|
||||
popdata: [[2000,12,'КК РБ 2004'],[2015,15,'КК РБ 2015'],[2024,18,'Мониторинг 2024']],
|
||||
trend: [{year:2000,count:12},{year:2015,count:15},{year:2024,count:18}],
|
||||
});
|
||||
|
||||
ids.vorobiny_sych = sp({
|
||||
group: 'Птицы', habitat: 'Хвойный лес',
|
||||
name_ru: 'Воробьиный сыч', name_be: 'Верабіны сычык', name_lat: 'Glaucidium passerinum',
|
||||
category: 'VU', by_category: 'III',
|
||||
description: 'Самая маленькая сова Европы — размером с дрозда. Активна как днём, так и ночью. Делает запасы пищи в дуплах на зиму. Голос — монотонный свист, слышный далеко в лесу.',
|
||||
fact: 'Несмотря на размер с воробья, воробьиный сыч охотится на добычу крупнее себя — полёвок и мелких птиц.',
|
||||
threats: ['Вырубка старых ельников', 'Исчезновение дупел'],
|
||||
conservation: 'Сохранение старовозрастных ельников. Вывешивание искусственных дупел.',
|
||||
where_to_see: 'Налибокская пуща, Беловежская пуща, Ельнянский заказник',
|
||||
biomass: 0.08,
|
||||
regions: ['grodno','vitebsk','minsk'],
|
||||
});
|
||||
|
||||
ids.krasnozobaya_kazarka = sp({
|
||||
group: 'Птицы', habitat: 'Река и озеро',
|
||||
name_ru: 'Краснозобая казарка', name_be: 'Чырванашыйная казарка', name_lat: 'Branta ruficollis',
|
||||
category: 'EN', by_category: 'II',
|
||||
description: 'Одна из самых красивых арктических гусей. В Беларуси встречается только во время миграций — зимует на Чёрном море. Численность катастрофически сокращается из-за охоты на зимовках.',
|
||||
fact: 'Краснозобая казарка гнездится исключительно в тундре Западной Сибири. Беларусь — транзитный коридор миграции.',
|
||||
threats: ['Охота на зимовках', 'Изменение климата', 'Беспокойство на миграционных стоянках'],
|
||||
conservation: 'Запрет охоты. Охрана мест остановок на миграции.',
|
||||
where_to_see: 'Полесские водохранилища, озёра Витебщины (пролёт)',
|
||||
biomass: 1.5,
|
||||
regions: ['gomel','brest','vitebsk'],
|
||||
});
|
||||
|
||||
ids.filin = sp({
|
||||
group: 'Птицы', habitat: 'Хвойный лес',
|
||||
name_ru: 'Филин', name_be: 'Пугач', name_lat: 'Bubo bubo',
|
||||
category: 'VU', by_category: 'II',
|
||||
description: 'Крупнейшая сова планеты. Ночной охотник, способный атаковать добычу весом до 3 кг. Охотничий участок пары — до 80 км². Низкое токование самца слышно за 4 км в тихую ночь.',
|
||||
fact: 'Филин — единственная птица, которая регулярно охотится на других хищников: ловит ястребов и даже молодых орланов.',
|
||||
threats: ['Фактор беспокойства', 'Гибель на ЛЭП', 'Оскудение кормовой базы'],
|
||||
conservation: 'Охрана гнёзд. Изоляция ЛЭП. Мониторинг популяции.',
|
||||
where_to_see: 'Беловежская пуща, ПЗ «Налибокский», НП «Припятский»',
|
||||
model: 'procedural', biomass: 2.7,
|
||||
regions: ['brest','grodno','gomel','minsk'],
|
||||
trend: [{year:1990,count:200},{year:2010,count:250},{year:2024,count:280}],
|
||||
});
|
||||
|
||||
ids.zhuravl_seryi2 = sp({
|
||||
group: 'Птицы', habitat: 'Болото',
|
||||
name_ru: 'Большой подорлик', name_be: 'Вялікі арол-крычун', name_lat: 'Clanga clanga',
|
||||
category: 'EN', by_category: 'II',
|
||||
description: 'Один из самых редких орлов Европы. Гнездится в старых лиственных лесах рядом с болотами и поймами. Питается амфибиями, мышевидными грызунами. Особенно уязвим из-за низкой плодовитости — одно яйцо в кладке.',
|
||||
fact: 'В мире гнездится менее 10 000 пар большого подорлика. Беларусь — один из ключевых регионов гнездования в Европе.',
|
||||
threats: ['Осушение болот и пойм', 'Вырубка пойменных лесов', 'Беспокойство'],
|
||||
conservation: 'Охрана гнёзд. Сохранение болотных массивов.',
|
||||
where_to_see: 'НП «Припятский», Полесский радиационно-экологический заповедник',
|
||||
biomass: 2.0,
|
||||
regions: ['gomel','brest','minsk'],
|
||||
popdata: [[2000,80,'КК РБ 2004'],[2015,90,'КК РБ 2015'],[2024,100,'Мониторинг 2024']],
|
||||
trend: [{year:2000,count:80},{year:2015,count:90},{year:2024,count:100}],
|
||||
});
|
||||
|
||||
/* ── МЛЕКОПИТАЮЩИЕ ──────────────────────────────────────────────────────── */
|
||||
|
||||
ids.zubr = sp({
|
||||
group: 'Млекопитающие', habitat: 'Широколиственный лес',
|
||||
name_ru: 'Зубр европейский', name_be: 'Зубр', name_lat: 'Bison bonasus',
|
||||
category: 'VU', by_category: 'III',
|
||||
description: 'Крупнейшее наземное млекопитающее Европы. Масса самца достигает 920 кг при высоте в холке 188 см. Вид был полностью истреблён в дикой природе к 1927 году — всё нынешнее поголовье (около 7000 особей в мире) происходит от 12 зубров из зоопарков. Беларусь сыграла ключевую роль в восстановлении вида.',
|
||||
fact: 'Зубр может прыгнуть в высоту до 2 м. Беловежская пуща — колыбель восстановления этого вида.',
|
||||
threats: ['Инбридинг из-за малой генетической базы', 'Болезни', 'Браконьерство'],
|
||||
conservation: 'Беловежская пуща — ключевой центр разведения. Программы реинтродукции в Европе.',
|
||||
where_to_see: 'Беловежская пуща (основная популяция), Налибокская пуща',
|
||||
model: 'procedural', biomass: 650,
|
||||
regions: ['brest','grodno','minsk','vitebsk'],
|
||||
popdata: [[1927,0,'Летопись'],[1960,150,'КК РБ'],[1990,850,'КК РБ 1993'],[2010,1500,'КК РБ 2015'],[2024,2000,'Мониторинг 2024']],
|
||||
trend: [{year:1927,count:0},{year:1960,count:150},{year:1990,count:850},{year:2010,count:1500},{year:2024,count:2000}],
|
||||
});
|
||||
|
||||
ids.rys = sp({
|
||||
group: 'Млекопитающие', habitat: 'Хвойный лес',
|
||||
name_ru: 'Рысь евразийская', name_be: 'Рысь', name_lat: 'Lynx lynx',
|
||||
category: 'NT', by_category: 'III',
|
||||
description: 'Крупнейший кошачий хищник Европы. Ведёт одиночный ночной образ жизни. Охотится главным образом на косуль и зайцев, реже на других оленей. Огромные лапы работают как снегоступы — рысь не проваливается в снег глубиной до 50 см.',
|
||||
fact: 'Рысь может прыгнуть на расстояние 7 м. Она единственная крупная кошка, живущая севернее 60° с.ш.',
|
||||
threats: ['Браконьерство', 'Фрагментация лесных массивов', 'Снижение кормовой базы'],
|
||||
conservation: 'Полный запрет охоты. Охрана крупных лесных массивов. GPS-мониторинг.',
|
||||
where_to_see: 'Налибокская пуща, Беловежская пуща, Освейский заказник',
|
||||
model: 'procedural', biomass: 22,
|
||||
regions: ['grodno','minsk','vitebsk','brest'],
|
||||
popdata: [[1990,350,'КК РБ 1993'],[2005,500,'КК РБ 2004'],[2015,650,'КК РБ 2015'],[2024,700,'Мониторинг 2024']],
|
||||
trend: [{year:1990,count:350},{year:2005,count:500},{year:2015,count:650},{year:2024,count:700}],
|
||||
});
|
||||
|
||||
ids.medved = sp({
|
||||
group: 'Млекопитающие', habitat: 'Хвойный лес',
|
||||
name_ru: 'Бурый медведь', name_be: 'Буры мядзведзь', name_lat: 'Ursus arctos',
|
||||
category: 'NT', by_category: 'III',
|
||||
description: 'Крупнейший хищник фауны Беларуси. Всеяден — рацион на 80% состоит из растительной пищи. Залегает в берлогу с ноября по март. В Беларуси обитает на северо-востоке, сохранились лишь периферийные популяции.',
|
||||
fact: 'Медведь чует запах еды с расстояния 20 км. Самка рожает медвежат в берлоге прямо в январе, весящих всего 500 г.',
|
||||
threats: ['Браконьерство', 'Фрагментация лесов', 'Деградация кормовой базы'],
|
||||
conservation: 'Запрет охоты. Охрана берлог. Мониторинг численности.',
|
||||
where_to_see: 'Полоцкий район Витебской области, Освейский заказник',
|
||||
model: 'procedural', biomass: 180,
|
||||
regions: ['vitebsk','mogilev'],
|
||||
popdata: [[1990,80,'КК РБ 1993'],[2005,70,'КК РБ 2004'],[2015,65,'КК РБ 2015'],[2024,60,'Мониторинг 2024']],
|
||||
trend: [{year:1990,count:80},{year:2005,count:70},{year:2015,count:65},{year:2024,count:60}],
|
||||
});
|
||||
|
||||
ids.norka = sp({
|
||||
group: 'Млекопитающие', habitat: 'Река и озеро',
|
||||
name_ru: 'Европейская норка', name_be: 'Еўрапейская норка', name_lat: 'Mustela lutreola',
|
||||
category: 'CR', by_category: 'I',
|
||||
description: 'Один из самых редких хищников Европы. Конкурентно вытеснена завезённой американской норкой. Держится у небольших рек с захламлёнными берегами. Отличается от американской норкой белым пятном на верхней губе.',
|
||||
fact: 'Европейская норка занесена в Красную книгу МСОП как находящаяся под критической угрозой исчезновения. В Беларуси обитает менее 500 особей.',
|
||||
threats: ['Конкуренция с американской норкой', 'Охота/отлов', 'Деградация береговой растительности'],
|
||||
conservation: 'Программа разведения в неволе. Отлов и стерилизация американской норки. Реинтродукция.',
|
||||
where_to_see: 'Полесье, бассейн Немана',
|
||||
biomass: 0.6,
|
||||
regions: ['brest','gomel','grodno'],
|
||||
popdata: [[1990,2000,'КК РБ 1993'],[2005,800,'КК РБ 2004'],[2015,400,'КК РБ 2015'],[2024,300,'Мониторинг 2024']],
|
||||
trend: [{year:1990,count:2000},{year:2005,count:800},{year:2015,count:400},{year:2024,count:300}],
|
||||
});
|
||||
|
||||
ids.vydra = sp({
|
||||
group: 'Млекопитающие', habitat: 'Река и озеро',
|
||||
name_ru: 'Речная выдра', name_be: 'Выдра', name_lat: 'Lutra lutra',
|
||||
category: 'NT', by_category: 'III',
|
||||
description: 'Полуводный хищник семейства куньих. Мастер плавания — способна задерживать дыхание на 4 минуты. Поедает рыбу, раков, лягушек. Индикатор чистоты водоёмов: там, где живёт выдра, вода чистая.',
|
||||
fact: 'Выдра скользит по снегу и льду на брюхе, развивая скорость до 25 км/ч. Каждый день ей нужно съесть 15% своего веса.',
|
||||
threats: ['Загрязнение водоёмов', 'Браконьерство', 'Вылов в рыболовные снасти'],
|
||||
conservation: 'Охрана водоохранных зон. Запрет охоты. Очистка водоёмов.',
|
||||
where_to_see: 'Реки по всей Беларуси, НП «Припятский»',
|
||||
biomass: 9,
|
||||
regions: ['brest','gomel','grodno','minsk','vitebsk','mogilev'],
|
||||
trend: [{year:1990,count:2500},{year:2010,count:3000},{year:2024,count:3200}],
|
||||
});
|
||||
|
||||
ids.bober = sp({
|
||||
group: 'Млекопитающие', habitat: 'Река и озеро',
|
||||
name_ru: 'Речной бобёр', name_be: 'Рачны бабёр', name_lat: 'Castor fiber',
|
||||
category: 'LC', by_category: 'IV',
|
||||
description: 'Крупнейший грызун Беларуси. Инженер экосистем: строит плотины, создаёт водоёмы, меняет ландшафт. Был истреблён к XIX веку и успешно реинтродуцирован. Сейчас численность в Беларуси — одна из крупнейших в Европе.',
|
||||
fact: 'Бобёр — природный мелиоратор: его плотины повышают уровень грунтовых вод, создают новые биотопы для других видов.',
|
||||
threats: ['Охота', 'Уничтожение прибрежной растительности'],
|
||||
conservation: 'Регулирование охоты. Охрана пойменных лесов.',
|
||||
where_to_see: 'Повсеместно у рек и каналов Беларуси',
|
||||
biomass: 25,
|
||||
regions: ['brest','gomel','grodno','minsk','vitebsk','mogilev'],
|
||||
trend: [{year:1960,count:1000},{year:1990,count:20000},{year:2010,count:50000},{year:2024,count:60000}],
|
||||
});
|
||||
|
||||
ids.zubatka = sp({
|
||||
group: 'Млекопитающие', habitat: 'Широколиственный лес',
|
||||
name_ru: 'Соня лесная', name_be: 'Лясная соня', name_lat: 'Dryomys nitedula',
|
||||
category: 'VU', by_category: 'III',
|
||||
description: 'Небольшой ночной грызун смешанных и лиственных лесов. Спит 7–8 месяцев в году — дольше всех млекопитающих Беларуси. Гнездо строит в дуплах или развилках ветвей на высоте 2–5 м.',
|
||||
fact: 'Перед зимней спячкой соня увеличивает массу тела вдвое, накапливая жир. Температура тела во сне падает до 1°C.',
|
||||
threats: ['Вырубка лиственных лесов', 'Снижение урожая орехов и желудей'],
|
||||
conservation: 'Охрана старолесий. Вывешивание дуплянок.',
|
||||
where_to_see: 'Беловежская пуща, Налибокская пуща',
|
||||
biomass: 0.03,
|
||||
regions: ['brest','grodno','minsk'],
|
||||
});
|
||||
|
||||
ids.kosulya = sp({
|
||||
group: 'Млекопитающие', habitat: 'Широколиственный лес',
|
||||
name_ru: 'Косуля европейская', name_be: 'Еўрапейская казуля', name_lat: 'Capreolus capreolus',
|
||||
category: 'LC', by_category: 'IV',
|
||||
description: 'Самый распространённый олень Беларуси. Важнейший элемент пищевой цепи — основная добыча волка, рыси и лисы. Рожки самец сбрасывает ежегодно в ноябре. Летом держится одиночно, зимой образует небольшие группы.',
|
||||
fact: 'Косуля — чемпион среди оленей по скорости: развивает до 60 км/ч. Рогатый самец защищает территорию площадью до 60 га.',
|
||||
threats: ['Интенсивная охота', 'Браконьерство', 'Хищники'],
|
||||
conservation: 'Регулирование охотничьей нагрузки. Мониторинг численности.',
|
||||
where_to_see: 'Лесные массивы по всей Беларуси',
|
||||
model: 'procedural', biomass: 25,
|
||||
regions: ['brest','gomel','grodno','minsk','vitebsk','mogilev'],
|
||||
trend: [{year:1990,count:80000},{year:2010,count:100000},{year:2024,count:110000}],
|
||||
});
|
||||
|
||||
ids.volk = sp({
|
||||
group: 'Млекопитающие', habitat: 'Хвойный лес',
|
||||
name_ru: 'Волк', name_be: 'Воўк', name_lat: 'Canis lupus',
|
||||
category: 'LC', by_category: 'IV',
|
||||
description: 'Крупнейший хищник семейства псовых. Живёт стаями 5–12 особей с чёткой иерархией. Регулирует численность копытных, оздоровляя их популяции. В Беларуси отношение к волку противоречивое — он и символ дикой природы, и угроза для скота.',
|
||||
fact: 'Волк за сутки способен пробежать до 70 км. Воя стаи достаточно, чтобы пометить территорию площадью в сотни км².',
|
||||
threats: ['Истребление как вредителя', 'Фрагментация ареала'],
|
||||
conservation: 'Мониторинг. Компенсации за ущерб скоту.',
|
||||
where_to_see: 'Леса по всей Беларуси',
|
||||
model: 'procedural', biomass: 40,
|
||||
regions: ['brest','gomel','grodno','minsk','vitebsk','mogilev'],
|
||||
trend: [{year:1990,count:1800},{year:2005,count:1500},{year:2015,count:1700},{year:2024,count:2000}],
|
||||
});
|
||||
|
||||
/* ── РАСТЕНИЯ ────────────────────────────────────────────────────────────── */
|
||||
|
||||
ids.venerina = sp({
|
||||
group: 'Растения', habitat: 'Широколиственный лес',
|
||||
name_ru: 'Венерин башмачок настоящий', name_be: 'Венерын чаравічак', name_lat: 'Cypripedium calceolus',
|
||||
category: 'EN', by_category: 'II',
|
||||
description: 'Красивейшая дикая орхидея Беларуси. Цветок напоминает туфельку. Растёт в тенистых лиственных лесах. Живёт до 100 лет, но впервые цветёт только в 15–17 лет. Опыляется только мелкими пчёлами рода Andrena.',
|
||||
fact: 'Венерин башмачок — самая долгоживущая травянистая орхидея. Одно растение в Германии было документально прослежено 100 лет на одном месте.',
|
||||
threats: ['Незаконный сбор', 'Вырубка лесов', 'Уплотнение почвы', 'Зарастание кустарником'],
|
||||
conservation: 'Охрана местонахождений. Культивирование в ботанических садах. Запрет сбора.',
|
||||
where_to_see: 'Беловежская пуща (ур. Новый Двор), Налибокская пуща',
|
||||
biomass: 0.05,
|
||||
regions: ['brest','grodno','minsk','vitebsk'],
|
||||
popdata: [[1990,5000,'КК РБ 1993'],[2005,3000,'КК РБ 2004'],[2015,2000,'КК РБ 2015'],[2024,1800,'Мониторинг 2024']],
|
||||
trend: [{year:1990,count:5000},{year:2005,count:3000},{year:2015,count:2000},{year:2024,count:1800}],
|
||||
});
|
||||
|
||||
ids.vodyanoy_oreh = sp({
|
||||
group: 'Растения', habitat: 'Река и озеро',
|
||||
name_ru: 'Водяной орех плавающий', name_be: 'Плывучы вадзяны арэх', name_lat: 'Trapa natans',
|
||||
category: 'CR', by_category: 'I',
|
||||
description: 'Реликт третичной флоры, живший на Земле ещё 100 миллионов лет назад. Плавающие листья образуют красивую розетку. Плоды съедобны, богаты крахмалом. Сохранился лишь в нескольких озёрах Полесья.',
|
||||
fact: 'Водяной орех пережил динозавров. Его плоды с четырьмя «рогами» служили пищей людям ещё в каменном веке.',
|
||||
threats: ['Загрязнение и эвтрофикация озёр', 'Колебания уровня воды', 'Конкуренция с другими водными растениями'],
|
||||
conservation: 'Охрана озёр. Ограничение хозяйственной деятельности. Культивирование.',
|
||||
where_to_see: 'Оз. Червоное (Брестская обл.), оз. Белое (Полесье)',
|
||||
biomass: 0.02,
|
||||
regions: ['brest','gomel'],
|
||||
popdata: [[1990,15000,'КК РБ 1993'],[2005,8000,'КК РБ 2004'],[2015,4000,'КК РБ 2015'],[2024,2500,'Мониторинг 2024']],
|
||||
trend: [{year:1990,count:15000},{year:2005,count:8000},{year:2015,count:4000},{year:2024,count:2500}],
|
||||
});
|
||||
|
||||
ids.palchatokorenik = sp({
|
||||
group: 'Растения', habitat: 'Болото',
|
||||
name_ru: 'Пальчатокоренник мясокрасный', name_be: 'Мяснякраснаты пальчатакарэнік', name_lat: 'Dactylorhiza incarnata',
|
||||
category: 'VU', by_category: 'III',
|
||||
description: 'Болотная орхидея с плотным колосом розово-лиловых цветков. Растёт на низинных болотах и заболоченных лугах. Микоризный гриб необходим для прорастания семян — без него орхидея не может развиться.',
|
||||
fact: 'Семена орхидей настолько малы (0,002 мм), что видны только под микроскопом. В одной коробочке содержится до 10 000 семян.',
|
||||
threats: ['Осушение болот', 'Зарастание кустарником', 'Выпас скота'],
|
||||
conservation: 'Охрана болотных массивов. Запрет мелиорации.',
|
||||
where_to_see: 'Полесье, Налибокская пуща, Осиповичский район',
|
||||
biomass: 0.03,
|
||||
regions: ['brest','gomel','grodno','minsk'],
|
||||
});
|
||||
|
||||
ids.molochai = sp({
|
||||
group: 'Растения', habitat: 'Луг и поле',
|
||||
name_ru: 'Прострел луговой', name_be: 'Сонца-трава', name_lat: 'Pulsatilla pratensis',
|
||||
category: 'VU', by_category: 'III',
|
||||
description: 'Один из первых весенних цветков. Тёмно-фиолетовые бокаловидные цветки появляются ещё при снеге. Покрыт шелковистыми волосками — защита от холода. Сильно сократился из-за сбора на букеты и распашки лугов.',
|
||||
fact: 'Прострел — символ весны в Беларуси. Второе название «сон-трава» — легенда гласит, что заснувший рядом с ним видит вещие сны.',
|
||||
threats: ['Сбор букетов', 'Распашка луговин', 'Сенокос до плодоношения'],
|
||||
conservation: 'Запрет сбора. Охрана луговых заказников.',
|
||||
where_to_see: 'Сосновые боры, остепнённые луга по всей Беларуси',
|
||||
biomass: 0.01,
|
||||
regions: ['brest','gomel','grodno','minsk','vitebsk','mogilev'],
|
||||
});
|
||||
|
||||
ids.lobelia = sp({
|
||||
group: 'Растения', habitat: 'Река и озеро',
|
||||
name_ru: 'Лобелия Дортмана', name_be: 'Лобелія Дортмана', name_lat: 'Lobelia dortmanna',
|
||||
category: 'EN', by_category: 'II',
|
||||
description: 'Водное растение олиготрофных озёр. Растёт на песчаном дне на глубине 0,5–3 м. Цветонос возвышается над водой. Сверхчувствителен к загрязнению — индикатор кристально чистой воды.',
|
||||
fact: 'Лобелия Дортмана поглощает CO₂ через корни из донных отложений, а не из воды — уникальный для растений способ.',
|
||||
threats: ['Эвтрофикация озёр', 'Загрязнение воды', 'Рекреационная нагрузка'],
|
||||
conservation: 'Охрана чистых озёр. Ограничение рекреации.',
|
||||
where_to_see: 'Браславские озёра, озёра Нарочанской группы',
|
||||
biomass: 0.01,
|
||||
regions: ['vitebsk','minsk','grodno'],
|
||||
});
|
||||
|
||||
ids.soldanella = sp({
|
||||
group: 'Растения', habitat: 'Широколиственный лес',
|
||||
name_ru: 'Лунник оживающий', name_be: 'Лунарыя ажываючая', name_lat: 'Lunaria rediviva',
|
||||
category: 'VU', by_category: 'III',
|
||||
description: 'Многолетнее растение тенистых влажных лесов. Серебристые эллиптические стручки используют для зимних букетов. Редчайшее в Беларуси — известно лишь несколько популяций.',
|
||||
fact: 'Название «лунник» происходит от формы плодов — круглых, как луна. После высыхания они становятся прозрачными и серебристыми.',
|
||||
threats: ['Сбор для флористики', 'Вырубка тенистых лесов'],
|
||||
conservation: 'Запрет сбора. Охрана местонахождений.',
|
||||
where_to_see: 'Налибокская пуща, Беловежская пуща',
|
||||
biomass: 0.1,
|
||||
regions: ['grodno','minsk','brest'],
|
||||
});
|
||||
|
||||
ids.matik = sp({
|
||||
group: 'Растения', habitat: 'Болото',
|
||||
name_ru: 'Шейхцерия болотная', name_be: 'Балотная шэйхцэрыя', name_lat: 'Scheuchzeria palustris',
|
||||
category: 'VU', by_category: 'III',
|
||||
description: 'Реликтовое травянистое растение верховых болот. Растёт в сфагновых зарослях у мочажин. Индикатор ненарушенных болотных экосистем. При осушении болот исчезает первым.',
|
||||
fact: 'Шейхцерия существует более 10 000 лет — с конца ледникового периода — практически не изменившись.',
|
||||
threats: ['Осушение болот', 'Торфоразработки'],
|
||||
conservation: 'Охрана верховых болот. Запрет торфоразработок.',
|
||||
where_to_see: 'Ельнянский заказник, Освейское болото',
|
||||
biomass: 0.01,
|
||||
regions: ['vitebsk','minsk','gomel'],
|
||||
});
|
||||
|
||||
/* ── НАСЕКОМЫЕ ────────────────────────────────────────────────────────────── */
|
||||
|
||||
ids.makhаon = sp({
|
||||
group: 'Насекомые', habitat: 'Луг и поле',
|
||||
name_ru: 'Махаон', name_be: 'Махаон', name_lat: 'Papilio machaon',
|
||||
category: 'NT', by_category: 'IV',
|
||||
description: 'Одна из крупнейших и красивейших бабочек Беларуси. Размах крыльев до 95 мм. Жёлтые крылья с чёрным рисунком и синей каймой. Гусеница поедает листья зонтичных растений. Стал редок из-за распашки лугов с дикими зонтичными.',
|
||||
fact: 'Хвостики-отростки на задних крыльях отвлекают хищников — птицы атакуют «ненастоящую голову».',
|
||||
threats: ['Распашка лугов с зонтичными растениями', 'Применение пестицидов', 'Сбор коллекционерами'],
|
||||
conservation: 'Сохранение лугов с дикими зонтичными. Запрет отлова.',
|
||||
where_to_see: 'Луга и опушки по всей Беларуси, Полесье',
|
||||
model: 'procedural', biomass: 0.002,
|
||||
regions: ['brest','gomel','grodno','minsk','vitebsk','mogilev'],
|
||||
trend: [{year:1990,count:100000},{year:2010,count:50000},{year:2024,count:30000}],
|
||||
});
|
||||
|
||||
ids.podolik = sp({
|
||||
group: 'Насекомые', habitat: 'Широколиственный лес',
|
||||
name_ru: 'Жук-олень', name_be: 'Жук-алень', name_lat: 'Lucanus cervus',
|
||||
category: 'VU', by_category: 'II',
|
||||
description: 'Крупнейший жук Беларуси. Самцы достигают 9 см. «Рога» — увеличенные жвала, используемые в турнирных боях. Личинка развивается в мёртвой древесине дуба 5–8 лет. Исчезает вместе со старыми дубравами.',
|
||||
fact: 'Жук-олень умеет летать, несмотря на тяжёлую «броню». Взрослый жук не ест — живёт всего 1–2 месяца за счёт жировых запасов.',
|
||||
threats: ['Вырубка старых дубрав', 'Уборка мёртвой древесины', 'Декоративный сбор'],
|
||||
conservation: 'Сохранение старовозрастных дубрав. Оставление валежника.',
|
||||
where_to_see: 'Беловежская пуща, Налибокская пуща',
|
||||
model: 'procedural', biomass: 0.008,
|
||||
regions: ['brest','grodno','minsk'],
|
||||
});
|
||||
|
||||
ids.apollon = sp({
|
||||
group: 'Насекомые', habitat: 'Луг и поле',
|
||||
name_ru: 'Аполлон', name_be: 'Апалон', name_lat: 'Parnassius apollo',
|
||||
category: 'EN', by_category: 'I',
|
||||
description: 'Крупная дневная бабочка с полупрозрачными белыми крыльями и красными глазками. Считается одним из красивейших насекомых Европы. В Беларуси сохранились единичные изолированные популяции. Гусеница питается очитком.',
|
||||
fact: 'Аполлон — первая бабочка, включённая в Конвенцию CITES (запрет международной торговли). Его коллекционируют вопреки всем запретам.',
|
||||
threats: ['Зарастание каменистых склонов кустарником', 'Коллекционирование', 'Применение пестицидов'],
|
||||
conservation: 'Строгая охрана популяций. Борьба с зарастанием склонов.',
|
||||
where_to_see: 'Окрестности Гродно (единственное местонахождение в РБ)',
|
||||
biomass: 0.003,
|
||||
regions: ['grodno'],
|
||||
popdata: [[2000,500,'КК РБ 2004'],[2015,300,'КК РБ 2015'],[2024,200,'Мониторинг 2024']],
|
||||
trend: [{year:2000,count:500},{year:2015,count:300},{year:2024,count:200}],
|
||||
});
|
||||
|
||||
ids.bogomol = sp({
|
||||
group: 'Насекомые', habitat: 'Луг и поле',
|
||||
name_ru: 'Богомол обыкновенный', name_be: 'Звычайны багамол', name_lat: 'Mantis religiosa',
|
||||
category: 'EN', by_category: 'II',
|
||||
description: 'Самый известный хищный кузнечик. Передние ноги — ловчий аппарат, реагирующий быстрее, чем успевает моргнуть человек. Самка после спаривания поедает самца. В Беларуси — на северной границе ареала.',
|
||||
fact: 'Богомол — единственное насекомое, способное поворачивать голову на 180°. Реакция захвата добычи занимает 0,05 секунды.',
|
||||
threats: ['Применение пестицидов', 'Распашка сухих лугов', 'Изменение климата'],
|
||||
conservation: 'Охрана сухих травянистых местообитаний.',
|
||||
where_to_see: 'Южная Беларусь — Брестская, Гомельская обл.',
|
||||
biomass: 0.004,
|
||||
regions: ['brest','gomel'],
|
||||
});
|
||||
|
||||
ids.stag_beetle2 = sp({
|
||||
group: 'Насекомые', habitat: 'Широколиственный лес',
|
||||
name_ru: 'Усач мускусный', name_be: 'Мускусны вусач', name_lat: 'Aromia moschata',
|
||||
category: 'VU', by_category: 'III',
|
||||
description: 'Ярко-зелёный жук с металлическим блеском. Издаёт сильный запах мускуса, слышный за несколько метров. Личинки развиваются в старых ивах. Встречается вдоль рек с ивняком.',
|
||||
fact: 'Мускусный усач — один из немногих жуков, использующих химическую сигнализацию для общения с партнёрами.',
|
||||
threats: ['Обрезка и уборка старых ив', 'Осушение пойм'],
|
||||
conservation: 'Сохранение старых ив вдоль рек.',
|
||||
where_to_see: 'Поймы рек Полесья, долина Немана',
|
||||
biomass: 0.004,
|
||||
regions: ['brest','gomel','grodno','minsk'],
|
||||
});
|
||||
|
||||
/* ── РЫБЫ ─────────────────────────────────────────────────────────────────── */
|
||||
|
||||
ids.sterlyadj = sp({
|
||||
group: 'Рыбы', habitat: 'Река и озеро',
|
||||
name_ru: 'Стерлядь', name_be: 'Асётр-стэрлядзь', name_lat: 'Acipenser ruthenus',
|
||||
category: 'EN', by_category: 'II',
|
||||
description: 'Самый мелкий осетр. Реликт доледникового периода — осетры появились 250 миллионов лет назад. В Беларуси исчезала в XX веке и восстанавливается искусственным зарыблением. Ценнейшая промысловая рыба.',
|
||||
fact: 'Осетры — живые ископаемые. Их форма тела не менялась 200 миллионов лет. Стерлядь доживает до 30 лет.',
|
||||
threats: ['Браконьерство', 'Строительство плотин', 'Загрязнение рек', 'Изъятие из русел гравия'],
|
||||
conservation: 'Искусственное воспроизводство. Рыборазводные заводы. Запрет вылова.',
|
||||
where_to_see: 'Р. Днепр, р. Припять, р. Сож (редко)',
|
||||
biomass: 0.8,
|
||||
regions: ['gomel','mogilev','brest'],
|
||||
popdata: [[1990,100,'КК РБ 1993'],[2010,500,'после зарыбления'],[2024,1500,'Мониторинг 2024']],
|
||||
trend: [{year:1990,count:100},{year:2010,count:500},{year:2024,count:1500}],
|
||||
});
|
||||
|
||||
ids.minog = sp({
|
||||
group: 'Рыбы', habitat: 'Река и озеро',
|
||||
name_ru: 'Ручьевая минога', name_be: 'Ручаёвая мінога', name_lat: 'Lampetra planeri',
|
||||
category: 'VU', by_category: 'III',
|
||||
description: 'Древнейший позвоночный — миноги появились 360 миллионов лет назад, ещё до динозавров. Не имеет челюстей. Личинка (амоцет) живёт в иле 3–7 лет с закрытыми глазами. Взрослая минога не питается и гибнет после нереста.',
|
||||
fact: 'Ручьевая минога — «живое ископаемое», практически не изменившееся за 360 миллионов лет. Её называют самым примитивным позвоночным.',
|
||||
threats: ['Загрязнение малых рек', 'Заиление нерестилищ', 'Мелиорация'],
|
||||
conservation: 'Охрана малых рек. Ограничение мелиорации.',
|
||||
where_to_see: 'Чистые малые реки Витебской, Гродненской областей',
|
||||
biomass: 0.02,
|
||||
regions: ['vitebsk','grodno','minsk','mogilev'],
|
||||
});
|
||||
|
||||
ids.usatch = sp({
|
||||
group: 'Рыбы', habitat: 'Река и озеро',
|
||||
name_ru: 'Усач обыкновенный', name_be: 'Марэна', name_lat: 'Barbus barbus',
|
||||
category: 'VU', by_category: 'III',
|
||||
description: 'Крупная реофильная рыба быстрых рек. Достигает 90 см. Ищет пищу на дне с помощью чувствительных усиков. Нерестится на каменистых перекатах — именно их исчезновение главная угроза.',
|
||||
fact: 'Икра усача ядовита для теплокровных — содержит ихтиотоксин. Местные рыбаки знали об этом и никогда её не ели.',
|
||||
threats: ['Добыча гравия', 'Строительство плотин', 'Загрязнение'],
|
||||
conservation: 'Сохранение каменистых перекатов. Запрет добычи гравия.',
|
||||
where_to_see: 'Р. Западный Буг, р. Нёман',
|
||||
biomass: 1.5,
|
||||
regions: ['brest','grodno'],
|
||||
});
|
||||
|
||||
ids.rylets = sp({
|
||||
group: 'Рыбы', habitat: 'Река и озеро',
|
||||
name_ru: 'Подуст обыкновенный', name_be: 'Падуст', name_lat: 'Chondrostoma nasus',
|
||||
category: 'EN', by_category: 'II',
|
||||
description: 'Стайная рыба горных и предгорных рек. В Беларуси — на восточной границе ареала. Питается водорослями и органикой, соскребая их нижнечелюстным роговым чехликом со дна.',
|
||||
fact: 'Подуст чистит дно реки, поедая водоросли — выполняет роль «пылесоса» экосистемы.',
|
||||
threats: ['Загрязнение', 'Уничтожение нерестовых субстратов', 'Строительство плотин'],
|
||||
conservation: 'Охрана нерестилищ. Запрет строительства.',
|
||||
where_to_see: 'Р. Западный Буг (редко)',
|
||||
biomass: 0.3,
|
||||
regions: ['brest'],
|
||||
});
|
||||
|
||||
/* ── РЕПТИЛИИ И АМФИБИИ ──────────────────────────────────────────────────── */
|
||||
|
||||
ids.cherepaha = sp({
|
||||
group: 'Рептилии и амфибии', habitat: 'Болото',
|
||||
name_ru: 'Черепаха болотная', name_be: 'Балотная чарапаха', name_lat: 'Emys orbicularis',
|
||||
category: 'VU', by_category: 'II',
|
||||
description: 'Единственная дикая черепаха Беларуси. Греется на берегах заболоченных водоёмов. При опасности мгновенно ныряет. Живёт до 100 лет. Откладывает яйца в прогретую почву. Самая северная черепаха Европы.',
|
||||
fact: 'Черепаха болотная определяет пол потомства температурой гнезда: при +28°C рождаются самки, при +26°C — самцы.',
|
||||
threats: ['Осушение болот', 'Уничтожение берегов', 'Гибель на дорогах'],
|
||||
conservation: 'Охрана водно-болотных угодий. Туннели под дорогами.',
|
||||
where_to_see: 'Полесье (Брестская, Гомельская обл.)',
|
||||
model: 'procedural', biomass: 0.5,
|
||||
regions: ['brest','gomel','grodno'],
|
||||
popdata: [[1990,10000,'КК РБ 1993'],[2005,7000,'КК РБ 2004'],[2015,5000,'КК РБ 2015'],[2024,4000,'Мониторинг 2024']],
|
||||
trend: [{year:1990,count:10000},{year:2005,count:7000},{year:2015,count:5000},{year:2024,count:4000}],
|
||||
});
|
||||
|
||||
ids.medyanka = sp({
|
||||
group: 'Рептилии и амфибии', habitat: 'Широколиственный лес',
|
||||
name_ru: 'Медянка', name_be: 'Медзянка', name_lat: 'Coronella austriaca',
|
||||
category: 'VU', by_category: 'III',
|
||||
description: 'Небольшая безвредная змея. Часто принимается за гадюку и уничтожается. Специализируется на ящерицах — конкурирует с гадюкой за пищу, но не ядовита. Гладкая чешуя с медным блеском.',
|
||||
fact: 'Медянка сжимает добычу как удав — потому что ящерицы не боятся яда. Укус человека абсолютно безвреден.',
|
||||
threats: ['Уничтожение из-за путаницы с гадюкой', 'Уничтожение сухих опушек'],
|
||||
conservation: 'Просветительская работа. Охрана сухих опушек и вырубок.',
|
||||
where_to_see: 'Сухие сосновые леса Полесья',
|
||||
biomass: 0.05,
|
||||
regions: ['brest','gomel','grodno','minsk'],
|
||||
});
|
||||
|
||||
ids.trit = sp({
|
||||
group: 'Рептилии и амфибии', habitat: 'Болото',
|
||||
name_ru: 'Тритон гребенчатый', name_be: 'Грэбеньчасты трытон', name_lat: 'Triturus cristatus',
|
||||
category: 'NT', by_category: 'III',
|
||||
description: 'Крупнейший тритон Беларуси. В брачный период самец развивает высокий зубчатый гребень от затылка до хвоста. Днём прячется в укрытиях, ночью охотится. Живёт у лесных водоёмов.',
|
||||
fact: 'Гребень самца — не для плавания, а для привлечения самок. В воде он играет роль «хвоста павлина».',
|
||||
threats: ['Мелиорация', 'Гибель на дорогах', 'Инвазивные рыбы в водоёмах'],
|
||||
conservation: 'Охрана прудов и болот. Ограничение вселения рыбы.',
|
||||
where_to_see: 'Лесные пруды по всей Беларуси',
|
||||
biomass: 0.02,
|
||||
regions: ['brest','gomel','grodno','minsk','vitebsk','mogilev'],
|
||||
});
|
||||
|
||||
/* ── ГРИБЫ ────────────────────────────────────────────────────────────────── */
|
||||
|
||||
ids.truffe = sp({
|
||||
group: 'Грибы', habitat: 'Широколиственный лес',
|
||||
name_ru: 'Трюфель летний', name_be: 'Летні трувель', name_lat: 'Tuber aestivum',
|
||||
category: 'EN', by_category: 'I',
|
||||
description: 'Подземный гриб — деликатес с неповторимым ароматом. Растёт в симбиозе с корнями дуба и бука. Плодовое тело полностью под землёй. В Беларуси — редчайший вид, известны единичные находки.',
|
||||
fact: 'Аромат трюфеля обусловлен андростенолом — феромоном, похожим на половой гормон кабана. Именно поэтому их традиционно ищут со свиньями.',
|
||||
threats: ['Вырубка дубрав', 'Уплотнение почвы', 'Сбор до созревания спор'],
|
||||
conservation: 'Охрана старых дубрав. Запрет сбора.',
|
||||
where_to_see: 'Беловежская пуща (единичные находки)',
|
||||
biomass: 0.1,
|
||||
regions: ['brest'],
|
||||
});
|
||||
|
||||
ids.mukhomor = sp({
|
||||
group: 'Грибы', habitat: 'Хвойный лес',
|
||||
name_ru: 'Решёточник красный', name_be: 'Чырвоная кратчатка', name_lat: 'Clathrus ruber',
|
||||
category: 'CR', by_category: 'I',
|
||||
description: 'Невероятный гриб — красная ажурная решётка. Вылупляется из белого яйца за несколько часов. Тухлый запах привлекает мух, распространяющих споры. Средиземноморский вид, в Беларуси единственная находка в 2019 г.',
|
||||
fact: 'Решёточник — один из самых необычных грибов мира. Его рост занимает всего 4–6 часов. Живёт 1–2 дня.',
|
||||
threats: ['Недостаточность тепла', 'Уничтожение при сборе'],
|
||||
conservation: 'Строгая охрана единственного местонахождения.',
|
||||
where_to_see: 'Гродненский район (единственная находка)',
|
||||
biomass: 0.05,
|
||||
regions: ['grodno'],
|
||||
});
|
||||
|
||||
ids.sparassis = sp({
|
||||
group: 'Грибы', habitat: 'Хвойный лес',
|
||||
name_ru: 'Спарасис курчавый (грибная капуста)', name_be: 'Кучаравы спарасіс', name_lat: 'Sparassis crispa',
|
||||
category: 'VU', by_category: 'III',
|
||||
description: 'Гриб, похожий на кочан цветной капусты кремового цвета. Диаметр до 50 см, вес до 5 кг. Растёт у оснований старых сосен. Съедобен и очень вкусен. Обладает противоопухолевыми свойствами.',
|
||||
fact: 'Спарасис содержит β-1,3-глюкан — вещество, доказанно усиливающее иммунитет и применяемое в японской медицине.',
|
||||
threats: ['Вырубка старых сосновых лесов', 'Сбор до созревания спор'],
|
||||
conservation: 'Сохранение старых сосняков. Ограничение сбора.',
|
||||
where_to_see: 'Старые сосняки Беларуси',
|
||||
biomass: 1.5,
|
||||
regions: ['brest','gomel','grodno','minsk','vitebsk','mogilev'],
|
||||
});
|
||||
|
||||
/* ── МХИ И ЛИШАЙНИКИ ─────────────────────────────────────────────────────── */
|
||||
|
||||
ids.lobaria = sp({
|
||||
group: 'Мхи и лишайники', habitat: 'Широколиственный лес',
|
||||
name_ru: 'Лобария лёгочная', name_be: 'Лёгачная лобарыя', name_lat: 'Lobaria pulmonaria',
|
||||
category: 'VU', by_category: 'III',
|
||||
description: 'Крупный лишайник — симбиоз гриба, водоросли и цианобактерии. Таллом похож на лёгочную ткань. Растёт только в старых нетронутых лесах с постоянной влажностью. Индикатор экологической ценности леса.',
|
||||
fact: 'Лобария лёгочная фиксирует атмосферный азот. Раньше её применяли при болезнях лёгких — отсюда и название.',
|
||||
threats: ['Вырубка старолесий', 'Загрязнение воздуха (SO₂)', 'Изменение микроклимата'],
|
||||
conservation: 'Охрана старовозрастных лесов.',
|
||||
where_to_see: 'Беловежская пуща, Налибокская пуща',
|
||||
biomass: 0.05,
|
||||
regions: ['brest','grodno'],
|
||||
});
|
||||
|
||||
ids.kladon = sp({
|
||||
group: 'Мхи и лишайники', habitat: 'Хвойный лес',
|
||||
name_ru: 'Кладония звёздчатая', name_be: 'Зорчатая кладонія', name_lat: 'Cladonia stellaris',
|
||||
category: 'VU', by_category: 'III',
|
||||
description: 'Напочвенный кустистый лишайник, образующий белые кочки в сосновых борах. Один из компонентов оленьего мха — важный корм для оленей зимой. Растёт крайне медленно — 1–3 мм в год.',
|
||||
fact: 'Ковёр из кладонии в сосняке может быть возрастом 200–300 лет. Протяжённость "колонии" до 10 м.',
|
||||
threats: ['Пожары', 'Вытаптывание', 'Загрязнение воздуха'],
|
||||
conservation: 'Охрана сухих сосняков. Противопожарные меры.',
|
||||
where_to_see: 'Сухие боры Витебской, Минской, Гомельской областей',
|
||||
biomass: 0.1,
|
||||
regions: ['vitebsk','minsk','gomel','brest'],
|
||||
});
|
||||
|
||||
db.exec('COMMIT');
|
||||
console.log(`✓ Виды засеяны (${Object.keys(ids).length} видов)`);
|
||||
} catch (e) {
|
||||
db.exec('ROLLBACK');
|
||||
console.error('Ошибка при вставке видов:', e.message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
ПИЩЕВАЯ СЕТЬ
|
||||
══════════════════════════════════════════════════════════════════════════ */
|
||||
const insWeb = db.prepare('INSERT OR IGNORE INTO rb_food_web (predator_id, prey_id, strength) VALUES (?,?,?)');
|
||||
|
||||
db.exec('BEGIN');
|
||||
const web = [
|
||||
// Орлан-белохвост охотится на рыб, водоплавающих птиц
|
||||
[ids.orlan, ids.sterlyadj, 0.6],
|
||||
[ids.orlan, ids.usatch, 0.3],
|
||||
[ids.orlan, ids.krasnozobaya_kazarka, 0.1],
|
||||
// Скопа — только рыба
|
||||
[ids.skopa, ids.sterlyadj, 0.7],
|
||||
[ids.skopa, ids.usatch, 0.3],
|
||||
[ids.skopa, ids.rylets, 0.2],
|
||||
// Змееяд — рептилии
|
||||
[ids.zmeeyed, ids.medyanka, 0.7],
|
||||
[ids.zmeeyed, ids.cherepaha, 0.1],
|
||||
// Филин
|
||||
[ids.filin, ids.kosulya, 0.2],
|
||||
[ids.filin, ids.bober, 0.1],
|
||||
[ids.filin, ids.vydra, 0.1],
|
||||
// Большой подорлик
|
||||
[ids.zhuravl_seryi2, ids.trit, 0.5],
|
||||
[ids.zhuravl_seryi2, ids.cherepaha, 0.3],
|
||||
[ids.zhuravl_seryi2, ids.minog, 0.2],
|
||||
// Рысь — косули, заяц, бобёр
|
||||
[ids.rys, ids.kosulya, 0.8],
|
||||
[ids.rys, ids.bober, 0.1],
|
||||
[ids.rys, ids.zubatka, 0.05],
|
||||
// Волк
|
||||
[ids.volk, ids.kosulya, 0.7],
|
||||
[ids.volk, ids.zubr, 0.1],
|
||||
[ids.volk, ids.bober, 0.2],
|
||||
// Выдра — рыба, лягушки
|
||||
[ids.vydra, ids.sterlyadj, 0.4],
|
||||
[ids.vydra, ids.minog, 0.3],
|
||||
[ids.vydra, ids.usatch, 0.3],
|
||||
// Норка — рыба, раки
|
||||
[ids.norka, ids.rylets, 0.4],
|
||||
[ids.norka, ids.minog, 0.3],
|
||||
[ids.norka, ids.trit, 0.3],
|
||||
// Бурый медведь — всеяден
|
||||
[ids.medved, ids.bober, 0.15],
|
||||
[ids.medved, ids.lobelia, 0.2],
|
||||
// Журавль — насекомые, лягушки, зерно
|
||||
[ids.zhuravl, ids.trit, 0.3],
|
||||
[ids.zhuravl, ids.bogomol, 0.2],
|
||||
// Черный аист — рыба, лягушки
|
||||
[ids.chorny_aist, ids.minog, 0.4],
|
||||
[ids.chorny_aist, ids.sterlyadj, 0.2],
|
||||
[ids.chorny_aist, ids.trit, 0.4],
|
||||
// Коростель — насекомые
|
||||
[ids.dergach, ids.makhаon, 0.1],
|
||||
[ids.dergach, ids.bogomol, 0.1],
|
||||
// Жук-олень — мёртвая дубовая древесина
|
||||
[ids.podolik, ids.lobaria, 0.1],
|
||||
];
|
||||
web.forEach(([p, q, s]) => insWeb.run(p, q, s));
|
||||
db.exec('COMMIT');
|
||||
console.log(`✓ Пищевая сеть: ${web.length} связей`);
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
КВЕСТЫ
|
||||
══════════════════════════════════════════════════════════════════════════ */
|
||||
const insQ = db.prepare('INSERT INTO rb_quests (title, description, species_ids, xp_reward, badge_slug) VALUES (?,?,?,?,?)');
|
||||
|
||||
[
|
||||
[
|
||||
'Спасти зубра',
|
||||
'Узнайте историю зубра — от истребления до триумфального возвращения. Пройдите 5 этапов: история вида, генетика, пищевая сеть, ареал и современная охрана.',
|
||||
JSON.stringify([ids.zubr, ids.kosulya, ids.volk, ids.rys]),
|
||||
200, 'quest_zubr'
|
||||
],
|
||||
[
|
||||
'Путь чёрного аиста',
|
||||
'Проследите миграцию чёрного аиста из белорусских болот в Африку. Изучите биомы, угрозы на маршруте и меры охраны.',
|
||||
JSON.stringify([ids.chorny_aist, ids.zhuravl, ids.cherepaha, ids.trit]),
|
||||
180, 'quest_black_stork'
|
||||
],
|
||||
[
|
||||
'Река живёт',
|
||||
'Стерлядь — символ здоровья белорусских рек. Узнайте, почему исчезают осетры и как восстановить их популяцию.',
|
||||
JSON.stringify([ids.sterlyadj, ids.minog, ids.usatch, ids.vydra, ids.norka]),
|
||||
160, 'quest_river'
|
||||
],
|
||||
[
|
||||
'Последний лес',
|
||||
'Беловежская пуща — последний первобытный лес Европы. Исследуйте её обитателей и поймите, почему этот лес уникален.',
|
||||
JSON.stringify([ids.zubr, ids.rys, ids.filin, ids.venerina, ids.podolik, ids.truffe]),
|
||||
250, 'quest_forest'
|
||||
],
|
||||
].forEach(args => insQ.run(...args));
|
||||
|
||||
console.log('✓ Квесты');
|
||||
console.log('\n✅ seed-red-book.js завершён успешно!');
|
||||
console.log(` Видов: ${Object.keys(ids).length}`);
|
||||
console.log(' Запустите: node src/db/migrate.js && node src/db/seed-red-book.js');
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,63 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const db = require('./db');
|
||||
|
||||
const dataDir = path.join(__dirname, '../../data');
|
||||
const files = fs.readdirSync(dataDir).filter(f => f.endsWith('.json'));
|
||||
|
||||
for (const file of files) {
|
||||
const data = JSON.parse(fs.readFileSync(path.join(dataDir, file), 'utf8'));
|
||||
console.log(`\nSeeding: ${file} (subject: ${data.subject})`);
|
||||
|
||||
const subject = db.prepare('SELECT id FROM subjects WHERE slug = ?').get(data.subject);
|
||||
if (!subject) {
|
||||
console.error(`Subject "${data.subject}" not found. Run migrate first.`);
|
||||
continue;
|
||||
}
|
||||
const subject_id = subject.id;
|
||||
|
||||
/* topics */
|
||||
const topicMap = {};
|
||||
const insertTopic = db.prepare(
|
||||
'INSERT OR IGNORE INTO topics (subject_id, name, order_index) VALUES (?, ?, ?)'
|
||||
);
|
||||
for (const t of data.topics) {
|
||||
insertTopic.run(subject_id, t.name, t.order);
|
||||
const row = db.prepare('SELECT id FROM topics WHERE subject_id = ? AND name = ?')
|
||||
.get(subject_id, t.name);
|
||||
topicMap[t.name] = row.id;
|
||||
}
|
||||
|
||||
/* questions + options inside a transaction (idempotent — skip existing by text) */
|
||||
const checkQ = db.prepare('SELECT id FROM questions WHERE subject_id = ? AND text = ?');
|
||||
const insertQ = db.prepare(
|
||||
'INSERT INTO questions (subject_id, topic_id, text, difficulty, explanation) VALUES (?, ?, ?, ?, ?)'
|
||||
);
|
||||
const insertO = db.prepare(
|
||||
'INSERT INTO options (question_id, text, is_correct, order_index) VALUES (?, ?, ?, ?)'
|
||||
);
|
||||
|
||||
let added = 0, skipped = 0;
|
||||
db.exec('BEGIN');
|
||||
try {
|
||||
for (const q of data.questions) {
|
||||
if (checkQ.get(subject_id, q.text)) { skipped++; continue; }
|
||||
const topic_id = topicMap[q.topic] ?? null;
|
||||
insertQ.run(subject_id, topic_id, q.text, q.difficulty, q.explanation ?? null);
|
||||
const { id: question_id } = db.prepare('SELECT last_insert_rowid() AS id').get();
|
||||
for (let i = 0; i < q.options.length; i++) {
|
||||
insertO.run(question_id, q.options[i], i === q.answer ? 1 : 0, i);
|
||||
}
|
||||
process.stdout.write('.');
|
||||
added++;
|
||||
}
|
||||
db.exec('COMMIT');
|
||||
} catch (err) {
|
||||
db.exec('ROLLBACK');
|
||||
throw err;
|
||||
}
|
||||
|
||||
console.log(`\n✓ Добавлено: ${added}, пропущено (уже есть): ${skipped}`);
|
||||
}
|
||||
|
||||
console.log('\nSeed complete.');
|
||||
Reference in New Issue
Block a user