Отчёт о занятии теперь начинается по-разному в зависимости от
номера темы: «начали знакомство» для первого занятия модуля (1/N),
«продолжили изучение» для промежуточного (2…N-1 из N) и
«завершили изучение» для последнего (N/N). Раньше первое
предложение всегда звучало одинаково, из-за чего первое и последнее
занятие модуля читались в отчёте для родителя одинаково.
Позицию разбирает воркер, а не модель. На локальной модели
(qwen2.5-1.5b) проверка показала, что с одной только подсказкой в
промпте «2/2» уверенно читается как «продолжали» вместо
«завершили», а в отчёте без номера темы модель выдумывала начало и
подмешивала «Scratch Jr» из примера. Поэтому номер разбирается
детерминированно и передаётся готовой строкой.
Что добавлено:
- lessonTopicPosition в worker.js — разбор «N/M» в конце свободного
текста темы: { kind, n, total, label }, kind = first | middle | last.
N = M проверяется раньше N = 1, поэтому «1/1» — это last. Мусорные
значения (N > M, M = 0, пустая тема) дают null, и строка подсказки
не добавляется. Хелпер экспортирован ради юнит-проверок
- в контекст запроса к модели добавлена строка
«Позиция темы: последнее занятие модуля (2 из 2)»; модель только
выбирает формулировку по ней
Промпт (LESSON_AI_DEFAULT_PROMPT, 2348 → 4523 симв.): новый блок
«НАЧАЛО ФРАЗЫ» с обязательными глаголами для каждого случая,
приоритет системной подсказки над «N/M», нейтральное начало без
номера, пример с тремя вариантами и запрет переносить «модуль» и
цифры в текст отчёта. Четыре копии промпта (server.js, db/init.sql,
db/migration.sql INSERT и UPDATE) синхронизированы побайтово; в
value IN миграции добавлен прежний дефолт, иначе правка не доехала
бы до существующих БД.
Проверено на живой модели: 1/2 → «начали знакомство», 2/2 →
«завершили изучение», 3/5 → «продолжили», 1/7 → «начали знакомство»,
без номера — нейтрально и без утечки из примера; e2e через
POST /api/lesson-reports с темой «2/2» дал ai_status = done.
AGENTS.md §3d: описание lesson_ai_prompt всё ещё называло его
«шаблоном делового сообщения», а список контекста воркера не
упоминал тему — исправлено, добавлены два пункта про позицию темы.
401 lines
21 KiB
PL/PgSQL
401 lines
21 KiB
PL/PgSQL
CREATE TABLE IF NOT EXISTS branches (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(200) NOT NULL UNIQUE,
|
|
address TEXT,
|
|
phone VARCHAR(50),
|
|
created_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS banned_ips (
|
|
ip VARCHAR(64) PRIMARY KEY,
|
|
reason VARCHAR(100) NOT NULL,
|
|
banned_until TIMESTAMPTZ NOT NULL,
|
|
created_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS groups (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(100) NOT NULL UNIQUE,
|
|
branch_id INT REFERENCES branches(id) ON DELETE SET NULL,
|
|
created_at TIMESTAMPTZ DEFAULT now(),
|
|
day_of_week INT,
|
|
time_start TIME,
|
|
time_end TIME,
|
|
cover_path VARCHAR(255),
|
|
deleted_at TIMESTAMPTZ,
|
|
purge_at TIMESTAMPTZ
|
|
);
|
|
|
|
ALTER TABLE groups ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
|
|
ALTER TABLE groups ADD COLUMN IF NOT EXISTS purge_at TIMESTAMPTZ;
|
|
|
|
CREATE TABLE IF NOT EXISTS modules (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(200) NOT NULL UNIQUE,
|
|
lessons_count INT NOT NULL DEFAULT 0,
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
photo_path VARCHAR(255),
|
|
created_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
|
|
ALTER TABLE modules ADD COLUMN IF NOT EXISTS is_active BOOLEAN NOT NULL DEFAULT true;
|
|
ALTER TABLE modules ADD COLUMN IF NOT EXISTS photo_path VARCHAR(255);
|
|
|
|
CREATE TABLE IF NOT EXISTS students (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(150) NOT NULL UNIQUE,
|
|
group_id INT REFERENCES groups(id),
|
|
created_at TIMESTAMPTZ DEFAULT now(),
|
|
photo_path VARCHAR(255),
|
|
profile JSONB
|
|
);
|
|
|
|
ALTER TABLE students ADD COLUMN IF NOT EXISTS photo_path VARCHAR(255);
|
|
ALTER TABLE students ADD COLUMN IF NOT EXISTS profile JSONB;
|
|
|
|
CREATE TABLE IF NOT EXISTS student_photos (
|
|
id SERIAL PRIMARY KEY,
|
|
student_id INT NOT NULL REFERENCES students(id) ON DELETE CASCADE,
|
|
photo_path VARCHAR(255) NOT NULL,
|
|
created_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_student_photos_student_id ON student_photos(student_id);
|
|
|
|
CREATE TABLE IF NOT EXISTS entries (
|
|
id SERIAL PRIMARY KEY,
|
|
student_name VARCHAR(150) NOT NULL,
|
|
group_id INT NOT NULL REFERENCES groups(id),
|
|
module_id INT REFERENCES modules(id) ON DELETE SET NULL,
|
|
description TEXT NOT NULL,
|
|
description_original TEXT,
|
|
description_ai TEXT,
|
|
ai_status VARCHAR(20) NOT NULL DEFAULT 'pending',
|
|
ai_checked_at TIMESTAMPTZ,
|
|
ai_error TEXT,
|
|
photo_path VARCHAR(255),
|
|
photo_original_path VARCHAR(255),
|
|
deleted_at TIMESTAMPTZ,
|
|
purge_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
|
|
ALTER TABLE entries ADD COLUMN IF NOT EXISTS description_original TEXT;
|
|
ALTER TABLE entries ADD COLUMN IF NOT EXISTS description_ai TEXT;
|
|
ALTER TABLE entries ADD COLUMN IF NOT EXISTS ai_status VARCHAR(20) NOT NULL DEFAULT 'pending';
|
|
ALTER TABLE entries ADD COLUMN IF NOT EXISTS ai_checked_at TIMESTAMPTZ;
|
|
ALTER TABLE entries ADD COLUMN IF NOT EXISTS ai_error TEXT;
|
|
ALTER TABLE entries ADD COLUMN IF NOT EXISTS module_id INT REFERENCES modules(id) ON DELETE SET NULL;
|
|
ALTER TABLE entries ADD COLUMN IF NOT EXISTS purge_at TIMESTAMPTZ;
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_entries_ai_pending ON entries(id) WHERE ai_status = 'pending' AND deleted_at IS NULL;
|
|
CREATE INDEX IF NOT EXISTS idx_entries_module_id ON entries(module_id);
|
|
|
|
CREATE OR REPLACE FUNCTION notify_entries_changed() RETURNS trigger AS $$
|
|
BEGIN
|
|
IF (TG_OP = 'INSERT') THEN
|
|
PERFORM pg_notify('entries_changed', json_build_object('type', 'entry_created', 'id', NEW.id)::text);
|
|
ELSIF (TG_OP = 'UPDATE' AND OLD.ai_status IS DISTINCT FROM NEW.ai_status) THEN
|
|
PERFORM pg_notify('entries_changed', json_build_object('type', 'ai_status', 'id', NEW.id, 'status', NEW.ai_status, 'error', NEW.ai_error, 'description', NEW.description, 'description_ai', NEW.description_ai, 'description_original', NEW.description_original)::text);
|
|
END IF;
|
|
RETURN NULL;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
DROP TRIGGER IF EXISTS trg_entries_notify ON entries;
|
|
CREATE TRIGGER trg_entries_notify AFTER INSERT OR UPDATE OF ai_status ON entries
|
|
FOR EACH ROW EXECUTE FUNCTION notify_entries_changed();
|
|
|
|
CREATE TABLE IF NOT EXISTS settings (
|
|
key TEXT PRIMARY KEY,
|
|
value TEXT
|
|
);
|
|
|
|
INSERT INTO settings (key, value) VALUES ('spam_interval_min', '30')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
INSERT INTO settings (key, value) VALUES ('ai_prompt', 'Ты — редактор текстов. Исправь ТОЛЬКО грамматические, орфографические и пунктуационные ошибки в тексте. Приведи к правильному регистру буквы. НЕ меняй слова, структуру предложений, стиль или смысл текста. Верни ТОЛЬКО исправленный текст без пояснений.')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
INSERT INTO settings (key, value) VALUES ('ai_autocheck_enabled', 'true')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
INSERT INTO settings (key, value) VALUES ('share_show_student_message', 'false')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
INSERT INTO settings (key, value) VALUES ('share_show_entry_date', 'false')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
INSERT INTO settings (key, value) VALUES ('share_show_student_names', 'true')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
INSERT INTO settings (key, value) VALUES ('share_show_group_photos', 'true')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
INSERT INTO settings (key, value) VALUES ('cookie_notice_text', 'Этот сайт использует cookie-файлы для корректной работы. Продолжая просмотр, вы соглашаетесь с их использованием.')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
INSERT INTO settings (key, value) VALUES ('camera_enabled', 'true')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
INSERT INTO settings (key, value) VALUES ('trash_purge_days', '30')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
INSERT INTO settings (key, value) VALUES ('timezone', 'Europe/Moscow')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
INSERT INTO settings (key, value) VALUES ('time_format', '24h')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
|
|
CREATE TABLE IF NOT EXISTS share_links (
|
|
id SERIAL PRIMARY KEY,
|
|
token VARCHAR(40) UNIQUE NOT NULL,
|
|
name VARCHAR(200) NOT NULL,
|
|
group_id INT REFERENCES groups(id),
|
|
student_name VARCHAR(150),
|
|
date_from DATE,
|
|
date_to DATE,
|
|
show_student_names BOOLEAN,
|
|
expires_at TIMESTAMPTZ,
|
|
access_password_hash VARCHAR(255),
|
|
message TEXT,
|
|
link_url VARCHAR(500),
|
|
show_student_message BOOLEAN,
|
|
show_entry_date BOOLEAN,
|
|
show_group_photos BOOLEAN,
|
|
created_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_share_links_expires_at ON share_links(expires_at);
|
|
|
|
INSERT INTO groups (name) VALUES
|
|
('Beginner'),
|
|
('Intermediate'),
|
|
('Advanced')
|
|
ON CONFLICT (name) DO NOTHING;
|
|
|
|
CREATE TABLE IF NOT EXISTS group_photos (
|
|
id SERIAL PRIMARY KEY,
|
|
group_id INT NOT NULL REFERENCES groups(id) ON DELETE CASCADE,
|
|
photo_path VARCHAR(255) NOT NULL,
|
|
caption TEXT,
|
|
taken_at DATE,
|
|
sort_order INT DEFAULT 0,
|
|
created_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_group_photos_group_id ON group_photos(group_id);
|
|
|
|
CREATE TABLE IF NOT EXISTS project_files (
|
|
id SERIAL PRIMARY KEY,
|
|
entry_id INT REFERENCES entries(id) ON DELETE CASCADE,
|
|
token VARCHAR(64) UNIQUE NOT NULL,
|
|
path VARCHAR(255) NOT NULL,
|
|
name VARCHAR(255) NOT NULL,
|
|
created_at TIMESTAMPTZ DEFAULT now(),
|
|
detached_at TIMESTAMPTZ
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS entry_photos (
|
|
id SERIAL PRIMARY KEY,
|
|
entry_id INT NOT NULL REFERENCES entries(id) ON DELETE CASCADE,
|
|
photo_path VARCHAR(255) NOT NULL,
|
|
caption TEXT,
|
|
sort_order INT DEFAULT 0,
|
|
created_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_entry_photos_entry_id ON entry_photos(entry_id);
|
|
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id SERIAL PRIMARY KEY,
|
|
username VARCHAR(100) NOT NULL UNIQUE,
|
|
password_hash VARCHAR(255) NOT NULL,
|
|
name VARCHAR(150),
|
|
role VARCHAR(20) NOT NULL DEFAULT 'tutor' CHECK (role IN ('admin','tutor')),
|
|
is_active BOOLEAN DEFAULT true,
|
|
created_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
|
|
ALTER TABLE groups ADD COLUMN IF NOT EXISTS tutor_id INT REFERENCES users(id) ON DELETE SET NULL;
|
|
|
|
CREATE TABLE IF NOT EXISTS user_branches (
|
|
user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
branch_id INT NOT NULL REFERENCES branches(id) ON DELETE CASCADE,
|
|
PRIMARY KEY (user_id, branch_id)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
id SERIAL PRIMARY KEY,
|
|
user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
token VARCHAR(64) NOT NULL UNIQUE,
|
|
created_at TIMESTAMPTZ DEFAULT now(),
|
|
expires_at TIMESTAMPTZ NOT NULL
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_sessions_token ON sessions(token);
|
|
CREATE INDEX IF NOT EXISTS idx_sessions_expires_at ON sessions(expires_at);
|
|
|
|
CREATE TABLE IF NOT EXISTS lesson_reports (
|
|
id SERIAL PRIMARY KEY,
|
|
group_id INT NOT NULL REFERENCES groups(id) ON DELETE CASCADE,
|
|
lesson_date DATE NOT NULL,
|
|
lesson_time TIME,
|
|
topic TEXT,
|
|
text TEXT NOT NULL,
|
|
text_original TEXT,
|
|
text_ai TEXT,
|
|
ai_status VARCHAR(20) NOT NULL DEFAULT 'none',
|
|
ai_checked_at TIMESTAMPTZ,
|
|
ai_error TEXT,
|
|
author_id INT REFERENCES users(id) ON DELETE SET NULL,
|
|
branch_id INT REFERENCES branches(id) ON DELETE SET NULL,
|
|
created_at TIMESTAMPTZ DEFAULT now(),
|
|
updated_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_lesson_reports_group_date ON lesson_reports(group_id, lesson_date);
|
|
CREATE INDEX IF NOT EXISTS idx_lesson_reports_date ON lesson_reports(lesson_date DESC);
|
|
|
|
CREATE TABLE IF NOT EXISTS lesson_report_versions (
|
|
id SERIAL PRIMARY KEY,
|
|
lesson_report_id INT NOT NULL REFERENCES lesson_reports(id) ON DELETE CASCADE,
|
|
text TEXT NOT NULL,
|
|
source VARCHAR(20) NOT NULL DEFAULT 'manual',
|
|
author_id INT REFERENCES users(id) ON DELETE SET NULL,
|
|
created_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_lesson_report_versions_report ON lesson_report_versions(lesson_report_id, id DESC);
|
|
|
|
CREATE TABLE IF NOT EXISTS photo_jobs (
|
|
id SERIAL PRIMARY KEY,
|
|
entry_id INT NOT NULL REFERENCES entries(id) ON DELETE CASCADE,
|
|
action VARCHAR(20) NOT NULL DEFAULT 'ai',
|
|
params JSONB,
|
|
before_path VARCHAR(255),
|
|
after_path VARCHAR(255),
|
|
status VARCHAR(20) NOT NULL DEFAULT 'pending',
|
|
applied BOOLEAN NOT NULL DEFAULT false,
|
|
attempts INT NOT NULL DEFAULT 0,
|
|
error TEXT,
|
|
created_at TIMESTAMPTZ DEFAULT now(),
|
|
finished_at TIMESTAMPTZ
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_photo_jobs_pending ON photo_jobs(id) WHERE status = 'pending';
|
|
CREATE INDEX IF NOT EXISTS idx_photo_jobs_entry_id ON photo_jobs(entry_id);
|
|
|
|
INSERT INTO settings (key, value) VALUES ('photo_worker_enabled', 'true')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
|
|
INSERT INTO settings (key, value) VALUES ('photo_ai_face_mode', 'off')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
|
|
INSERT INTO settings (key, value) VALUES ('photo_ai_face_model', 'gfpgan')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
|
|
INSERT INTO settings (key, value) VALUES ('photo_ai_device_pref', 'auto')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
|
|
CREATE TABLE IF NOT EXISTS audit_log (
|
|
id SERIAL PRIMARY KEY,
|
|
user_id INT REFERENCES users(id) ON DELETE SET NULL,
|
|
action VARCHAR(100) NOT NULL,
|
|
target JSONB,
|
|
ip VARCHAR(45),
|
|
created_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_audit_log_created_at ON audit_log(created_at DESC);
|
|
|
|
CREATE TABLE IF NOT EXISTS notifications (
|
|
id SERIAL PRIMARY KEY,
|
|
type VARCHAR(50) NOT NULL,
|
|
level VARCHAR(20) NOT NULL DEFAULT 'info',
|
|
title VARCHAR(200) NOT NULL,
|
|
body TEXT,
|
|
link VARCHAR(255),
|
|
target JSONB,
|
|
admin_only BOOLEAN NOT NULL DEFAULT false,
|
|
branch_id INT REFERENCES branches(id) ON DELETE SET NULL,
|
|
created_at TIMESTAMPTZ DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_notifications_created_at ON notifications(created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_notifications_branch_id ON notifications(branch_id);
|
|
|
|
CREATE TABLE IF NOT EXISTS notification_reads (
|
|
user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
notification_id INT NOT NULL REFERENCES notifications(id) ON DELETE CASCADE,
|
|
read_at TIMESTAMPTZ DEFAULT now(),
|
|
PRIMARY KEY (user_id, notification_id)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_notification_reads_user ON notification_reads(user_id);
|
|
|
|
INSERT INTO settings (key, value) VALUES ('notify_enabled', 'true')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
INSERT INTO settings (key, value) VALUES ('notify_retention_days', '30')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
INSERT INTO settings (key, value) VALUES ('notify_entry_new', 'true')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
INSERT INTO settings (key, value) VALUES ('notify_entry_ai_corrected', 'false')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
INSERT INTO settings (key, value) VALUES ('notify_entry_ai_error', 'true')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
INSERT INTO settings (key, value) VALUES ('notify_photo_job_done', 'true')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
INSERT INTO settings (key, value) VALUES ('notify_photo_job_error', 'true')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
INSERT INTO settings (key, value) VALUES ('notify_ip_ban', 'true')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
INSERT INTO settings (key, value) VALUES ('notify_backup_restore', 'true')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
INSERT INTO settings (key, value) VALUES ('notify_backup_create', 'false')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
INSERT INTO settings (key, value) VALUES ('notify_system_test', 'true')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
|
|
INSERT INTO settings (key, value) VALUES ('lesson_ai_enabled', 'true')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
INSERT INTO settings (key, value) VALUES ('lesson_ai_prompt', 'Ты — редактор сообщений тьютора детской IT-школы.
|
|
|
|
Переработай исходный текст занятия так, чтобы он звучал естественно и грамотно, как будто его написал живой тьютор родителю, а не нейросеть.
|
|
|
|
ГЛАВНОЕ:
|
|
- Не добавляй информацию, которой нет в исходном тексте. Сохрани все факты и смысл.
|
|
- Исправь ошибки, повторы и неудачные формулировки. Убери канцелярит, шаблонные фразы и «ИИ-язык».
|
|
- Не используй чрезмерную похвалу и не превращай обычное занятие в достижение мирового масштала.
|
|
|
|
СТИЛЬ И СТРУКТУРА:
|
|
- Один цельный абзац, 2–4 предложения, от третьего лица.
|
|
- Первое предложение всегда начинай со слов «На занятии ребята …». Чем занимались в начале занятия — начало модуля, продолжение или завершение — определяй по правилу НАЧАЛО ФРАЗЫ.
|
|
- Если в исходном тексте описана практическая часть, начни её со слов «В конце занятия …» или «Затем …». Нет практики в исходнике — не придумывай её.
|
|
- Вместо «они» пиши «каждый». Обращение на «вы» не используй.
|
|
- Коротко скажи, с чем познакомились или что изучали; затем — что конкретно делали; в конце — что сделал самостоятельно.
|
|
- Сократи перечисления, объединяй их через «и», «а также», не повторяй одно и то же разными словами.
|
|
- Убери разговорные и оценочные обороты: «было весело», «очень», «классно».
|
|
|
|
НАЧАЛО ФРАЗЫ (определяется номером темы):
|
|
Система присылает готовую подсказку строкой «Позиция темы: …». Если такой строки нет, смотри номер вида N/M в конце строки «Тема занятия: …», где N — номер занятия в модуле, M — сколько занятий в модуле всего. Пользуйся только одной из этих подсказок и всегда проверяй, что N не больше M.
|
|
- «последнее занятие модуля» (N = M, например 2/2 или 7/7): занятие завершает модуль. Первое предложение обязательно начни с «завершили» или «закончили»: «На занятии ребята завершили изучение …», «На занятии ребята закончили знакомство с …».
|
|
- «промежуточное занятие модуля» (1 < N < M, например 2/4, 3/5, 6/7): изучение продолжается. Первое предложение обязательно начни с «продолжили», «продолжали», «закрепили» или «углубили»: «На занятии ребята продолжили изучение …», «На занятии ребята закрепили …».
|
|
- «первое занятие модуля» (N = 1 и N < M, например 1/4 или 1/7): модуль начинается. Первое предложение обязательно начни с «начали» или «приступили»: «На занятии ребята начали знакомство с …», «На занятии ребята начали изучение …».
|
|
- Если подсказки нет или номер нечитаем — характер занятия не выбирай и не додумывай. Начни нейтрально, прямо с сути: «На занятии ребята разобрались …», «На занятии ребята отработали …», «На занятии ребята попробовали …».
|
|
- Это требование сильнее всего остального: даже если начало исходного текста уже хорошо звучит, перепиши его по правилу выше. Слово «модуль», цифры номера и любые оценки в текст отчёта не переноси: номер темы — внутренний признак для выбора формулировки.
|
|
|
|
ФОРМАТ:
|
|
- Без заголовков, списков, markdown, подписей и пояснений.
|
|
- Не начинай со слов «Сегодня», «Вчера», «Дата», «Группа», «Время», «Позиция» и вообще не упоминай группу, дату, время и номер занятия.
|
|
- Не начинай с «На данном занятии» или «В рамках занятия».
|
|
- Не оборачивай ответ в кавычки.
|
|
- Не добавляй лишних предложений: если исходный текст уже написан нормально, не переписывай его ради переписывания.
|
|
|
|
ПРИМЕР ПРЕОБРАЗОВАНИЯ (бери отсюда только формулировки, тему и факты примера в свой текст не переноси):
|
|
Исходный текст:
|
|
«Ребята познакомились с программой Scratch Jr, научились выбирать фон, добавлять, изменять и создавать своих персонажей. В завершении занятия они выполнили практическое индивидуальное задание по созданию собственной анимации и небольшой программы».
|
|
Тема занятия: «Анимация 1/3»
|
|
Позиция темы: первое занятие модуля (1 из 3)
|
|
Хороший результат:
|
|
«На занятии ребята начали знакомство со Scratch Jr: научились выбирать фон, добавлять и изменять персонажей, а также создавать своих героев. В конце занятия каждый самостоятельно выполнил небольшое практическое задание — придумал свою анимацию и собрал простую программу».
|
|
Тот же исходный текст, но «Позиция темы: промежуточное занятие модуля (2 из 3)» — меняется только начало:
|
|
«На занятии ребята продолжили знакомство со Scratch Jr: научились выбирать фон, добавлять и изменять персонажей…».
|
|
Тот же исходный текст, но «Позиция темы: последнее занятие модуля (3 из 3)»:
|
|
«На занятии ребята завершили знакомство со Scratch Jr: научились выбирать фон, добавлять и изменять персонажей…».
|
|
|
|
ГЛАВНОЕ ПРАВИЛО:
|
|
Отрабатывай ровно по этому исходному тексту. Ничего из примера выше в свой текст не переноси: тема, программа, персонажи и детали из примера не твои.')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
INSERT INTO settings (key, value) VALUES ('notify_lesson_ai_formatted', 'false')
|
|
ON CONFLICT (key) DO NOTHING;
|