diff --git a/TODO.md b/TODO.md index bee12a2..3459745 100644 --- a/TODO.md +++ b/TODO.md @@ -82,4 +82,5 @@ | 2026-09-15 | «✅ Опубликовать» — НИЖЕ поля комментария рядом с «💾 Сохранить комментарий»; оригинальный пост растянут на всю высоту панели | ✅ закрыта | коммит b542e9b | | 2026-09-15 | **Пайплайн «Кандидаты → Отобранные → Опубликованные»**: статус selected, /selected отдельный список, в кандидатах только «⭐ Отобрать», вся обработка в отобранных, классификация по кнопке | ✅ закрыта | change selected-list (коммит ae8af8d) | | 2026-09-15 | Подготовка своего поста в «Отобранных»: поле «✍️ Мой пост» (rewritten_text всегда видно) рядом с оригиналом — черновик «Переписать» редактируется и публикуется | ✅ закрыта | change selected-list (коммит ae8af8d) | -| 2026-09-17 | Фикс: при открытии поста (?selected=N) чекбоксы массового выбора на /candidates и /selected сбрасывались, оставался только чекбокс открытого поста. Нужно сохранять отметки при навигации для быстрого массового отказа/отбора | ✅ закрыта | change keep-bulk-checkboxes (sessionStorage vesti_bulk_candidates/vesti_bulk_selected; коммит c8fe5f7, запушен в gitverse; баг-фикс: селектор input[name=ids] вместо #bulk-form input) | \ No newline at end of file +| 2026-09-17 | Фикс: при открытии поста (?selected=N) чекбоксы массового выбора на /candidates и /selected сбрасывались, оставался только чекбокс открытого поста. Нужно сохранять отметки при навигации для быстрого массового отказа/отбора | ✅ закрыта | change keep-bulk-checkboxes (sessionStorage vesti_bulk_candidates/vesti_bulk_selected; коммит c8fe5f7, запушен в gitverse; баг-фикс: селектор input[name=ids] вместо #bulk-form input) | +| 2026-09-18 | Перевод поста на русский в карточках (кандидатов и отобранных): метка «Оригинал» → закладка, кнопка «🌐 Перевести» → закладка «Перевод». Перевод локальной моделью qwen3:8b-nothink, результат в posts.translated_text (идемпотентный ALTER при старте). Повторный клик без вызова модели. call_ollama не подошла (парсит JSON) — прямой запрос к Ollama /v1 | ✅ закрыта | change post-translate (коммит 4a166fa, запушен в gitverse; e2e на реальной модели пройден) | \ No newline at end of file diff --git a/WALKTHROUGH.md b/WALKTHROUGH.md index 94cbfb0..e642fea 100644 --- a/WALKTHROUGH.md +++ b/WALKTHROUGH.md @@ -621,3 +621,53 @@ sudo systemctl restart vesti-web && systemctl is-active vesti-web # active ### Коммит - `c8fe5f7` — fix(web): keep bulk checkbox selection across post navigation. Запушен в gitverse (8777a0c..c8fe5f7). - В рабочем дереве остаются чужие незакоммиченные наработки (crawler/*, classifier/*, sources/* и др.) — НЕ трогал. + +## 2026-09-18 — Сессия: перевод поста на русский в карточках (change post-translate) + +### Задача пользователя +«Добавить перевод в карточку поста (и кандидата, и отобранного). Метка "Оригинал" — сделать закладкой, +рядом кнопка "Перевести", создающая вторую закладку "Перевод" с переводом на русский. Справится ли локальная модель?» + +### Решение (через OpenSpec) +- change `post-translate` (proposal/design/tasks/specs/post-translate), `openspec validate` — чисто. +- Ответ на вопрос пользователя: ДА, локальная модель справляется (qwen3:8b-nothink на Ollama). + +### Код +1. **Миграция**: `_migrate()` в web/app.py — идемпотентный `ALTER TABLE posts ADD COLUMN translated_text TEXT` + (проверка через `PRAGMA table_info(posts)` перед ALTER; при старте веба). +2. **Helper** `_translate_text(text)` — прямой запрос к Ollama `/v1/chat/completions` (model qwen3:8b-nothink, + temperature 0.2, `think: False`, timeout из classifier TIMEOUT). Лимит `TRANSLATE_MAX_CHARS = 6000`. + - Промпт: «Переведи новость на русский. Сохрани смысл, факты, имена, числа и ссылки. Верни ТОЛЬКО перевод…». +3. **Роут** `POST /posts/{post_id}/translate` — после post_rewrite; если `translated_text` уже заполнен — + просто редирект (без вызова модели!); иначе переводит, `UPDATE`, редирект на `_cand_back(..., selected=post_id)`. + Ошибка модели → редирект с `error=translate:{e}` (рендерится как alert). +4. **Шаблоны** candidates.html и selected.html: метка «Оригинальный пост» → закладки + (nav-pills + tab-pane, vanilla JS в scripts-блоке — БЕЗ CDN/Bootstrap JS, как остальной проект). + - Нет перевода: одна закладка «Оригинал» + кнопка «🌐 Перевести». + - Есть перевод: закладки «Оригинал» и «Перевод» (активна «Перевод»), кнопка скрыта. + - candidates.html: `id="tab-orig-c"/"tab-tr-c"`; selected.html: `tab-orig-s"/"tab-tr-s"`, перевод + в правой колонке (col-md-6) с той же логикой. + +### Питфол (важно!) +- `call_ollama` из classifier/classify.py НЕ подходит для перевода: она жёстко парсит ответ модели как JSON + (это для классификации). Перевод — обычный текст → JSON-парсинг ломался (пустой результат). + Решение: прямой `httpx.post` к `{OLLAMA_URL}/v1/chat/completions` в `_translate_text`. + +### Проверка +- Модель жива: `curl -s -m 8 http://127.0.0.1:11434/v1/chat/completions -d '{"model":"qwen3:8b-nothink",...}'` → «Привет, мир». +- `_translate_text()` на реальном тексте: «LWN.net сообщает о последнем выпуске ядра Linux 6.12…». +- E2E TestClient: логин (пароль из .env) → POST /posts/123/translate → translated_text в БД (Kali Purple, 454 символа, + markdown и ссылки сохранены). +- Повторный клик: подмена `_translate_text` показала 0 вызовов модели (translated_text уже есть) ✓. +- Живой сервис: `sudo systemctl restart vesti-web` → active; страницы отдают закладки (curl с cookie из /login). + +### Коммит +- `4a166fa` — feat(web): перевод поста на русский локальной моделью (openspec post-translate). Запушен + на gitverse (`745680a..4a166fa main -> main`, ключ .ssh_box/gitverse). +- 9 файлов: web/app.py, candidates.html, selected.html, STATUS.md, openspec/changes/post-translate/* (5). +- Чужие незакоммиченные наработки (extract-post-sources, manual-direction, crawler/*, classifier/* и др.) + НЕ трогал — коммитился только свой change (git add точечный). + +## 2026-09-18 — Закрытие сессии +- Задача post-translate полностью закрыта (коммит + пуш). Обновлены STATUS.md, TODO.md, WALKTHROUGH.md. +- Открытые задачи проекта — в TODO.md (метрики-инсайты, fan-out направлений, веб-скедулинг RSS, извлечение источников).