openspec: архив 14 завершённых change-ов (веб-фиксы, crawler-queue, own-content-hub, publisher-service); спеки влиты в openspec/specs

This commit is contained in:
kpa39l
2026-09-16 17:01:00 +00:00
parent 584582a48c
commit 771f6a8276
88 changed files with 1632 additions and 3 deletions
@@ -0,0 +1,3 @@
schema: spec-driven
skip_specs: true
created: 2026-09-14
@@ -0,0 +1,62 @@
## Design
Файл: `/opt/vesti/web/app.py`, функция `_fetch_candidates`.
Текущий код (строки ~219-227):
```python
grouped = []
for k, it in itertools.groupby(sorted(flat, key=keyf), key=keyf):
items = list(it)
grouped.append({
"key": k,
"label": _group_label(group_by, k),
"status": items[0]["status"], # ← БАГ: статус первого поста
"posts": items,
})
```
Проблема: `items[0]["status"]` берёт статус первого поста в группе. При
`group_by=date` сортировка идёт по дате (свежие сверху), и первый пост группы —
свежайший, который может быть `published`/`rejected`, хотя вся остальная группа
состоит из `new`. Шаблон рисует бейдж группы только для `new`/`rejected`,
поэтому группа с первым published-постом остаётся без бейджа и выглядит как
«опубликованная».
Правка — считать статус группы как доминирующий среди всех постов группы:
```python
def _group_status(items):
"""Статус группы: приоритет new > rejected > published > первый."""
st = [p["status"] for p in items]
for s in ("new", "rejected", "published"):
if s in st:
return s
return items[0]["status"]
grouped = []
for k, it in itertools.groupby(sorted(flat, key=keyf), key=keyf):
items = list(it)
grouped.append({
"key": k,
"label": _group_label(group_by, k),
"status": _group_status(items),
"posts": items,
})
```
Логика приоритета: если в группе есть хотя бы один `new` — группа «новые»
(это важно для группировки по дате, где почти всегда есть новые кандидаты
наряду с опубликованными/отклонёнными). Если новых нет, но есть отклонённые —
«откл.». Иначе «опубл.» (для фильтра status=published).
## Верификация
- `openspec validate fix-date-group-status` — чисто.
- Юнит-проверка (без перезапуска сервиса):
`python -c "import sys; sys.path.insert(0,'/opt/vesti'); import web.app as A; c=A._db(); g,_,_=A._fetch_candidates(c,'','','','','date',limit=200); [print(x['key'],x['status']) for x in g]; c.close()"`
→ группа `week` должна иметь `status='new'` (не `published`).
- Перезапуск: `sudo systemctl restart vesti-web`.
- Ручная проверка в браузере: `GET /candidates?group_by=date` → группа
«Ранее на этой неделе» показывает бейдж «новые»; правая панель выбранного
поста корректно показывает кнопку «Опубликовать», если пост ещё не опубликован.
@@ -0,0 +1,51 @@
## Why
При переключении группировки списка кандидатов на «дата» (`/candidates?group_by=date`)
статусные бейджи групп и правая панель вводят в заблуждение: часть групп
(например, «Ранее на этой неделе», 52 поста) не получает бейджа статуса,
а выбранный пост в правой панели может выглядеть как уже «опубликованный»
(кнопка «Опубликовать» скрыта), хотя это — обычный кандидат.
Причина: в `_fetch_candidates` статус группы вычисляется как
`items[0]["status"]` — статус ПЕРВОГО (свежайшего) поста в группе.
При группировке по дате первым в группе оказывается самый свежий пост,
который может быть уже опубликованным или отклонённым, хотя вся остальная
группа — новые кандидаты. Шаблон `candidates.html` рисует бейдж только для
`new`/`rejected`, поэтому группа с первым published-постом выглядит «без
статуса» (как опубликованная), а у selected-published поста скрыта кнопка
«Опубликовать».
Наблюдаемый симптом пользователя: «при переключении на сортировку по дате
все кандидаты становятся отмеченными опубликовано и источник „Дед в АйТи“».
## What Changes
- В `web/app.py` (`_fetch_candidates`): статус группы считать не по первому
посту, а как ДОМИНИРУЮЩИЙ статус внутри группы:
- если в группе есть `new` → статус группы `new`;
- иначе если есть `rejected` → `rejected`;
- иначе если есть `published` → `published`;
- иначе — статус первого поста (запасной вариант).
- Шаблон `candidates.html` остаётся без изменений (для `published` бейдж не
рисуется — это корректно: опубликованные не показываются в кандидатах как
активные). Исправление логики устраняет и ложный «published»-вид группы, и
неправильный вид правой панели для не-публикованных постов.
- При `group_by=date` группа «Ранее на этой неделе» с 52 постами (51 new +
1 published) теперь получит бейдж «новые».
## Why Not
- Не менять сортировку постов внутри группы на статус: это сломает ожидание
«свежие сверху» для группировки по дате.
- Не прятать published-посты из date-группировки полностью: пользователь
должен видеть, что именно опубликовано в этот период.
- Минимальная правка в одном месте (`_fetch_candidates`) — не трогаем шаблон
и не меняем схему БД.
## Impact
- Файл: `web/app.py`, функция `_fetch_candidates`, блок вычисления
`"status"` группы.
- Данные: без миграций БД.
- Сервис: `vesti-web` (:8400) — требуется перезапуск.
- Rollback: откатить правку в `_fetch_candidates` и перезапустить сервис.
@@ -0,0 +1,10 @@
# fix-date-group-status
- [x] Создан OpenSpec change (proposal/design) — `skip_specs: true`, без delta-спеки (багфикс без изменения поведения контракта)
- [x] web/app.py: статус группы в `_fetch_candidates` — доминирующий по группе (new > rejected > published)
- [x] `openspec validate fix-date-group-status` — чисто
- [x] Юнит-проверка: `_fetch_candidates(...,'date')` → группа week имеет status='new'
- [x] Перезапуск vesti-web (`sudo systemctl restart vesti-web`)
- [x] Ручная проверка: `GET /candidates?group_by=date` — группа «Ранее на этой неделе» с бейджем «новые»; у selected не-опубликованного поста есть кнопка «Опубликовать»
- [x] Обновить STATUS.md / TODO.md
- [x] Бэкап: `sudo /opt/vesti/backup.sh`