feat(candidates): флаг прочитанности (is_read), жирные непрочитанные, голубая заливка выбранного, дефолтная группировка по дате

This commit is contained in:
kpa39l
2026-09-15 16:42:55 +00:00
parent 562d787f1b
commit c09dff08ac
8 changed files with 211 additions and 5 deletions
+23 -3
View File
@@ -75,6 +75,21 @@ def _db() -> sqlite3.Connection:
return conn
def _migrate():
"""Идемпотентные миграции при старте веб-сервиса."""
conn = sqlite3.connect(DB_PATH, timeout=10)
try:
cols = [r[1] for r in conn.execute("PRAGMA table_info(posts)").fetchall()]
if "is_read" not in cols:
conn.execute("ALTER TABLE posts ADD COLUMN is_read INTEGER DEFAULT 0")
conn.commit()
finally:
conn.close()
_migrate()
def _session_ok(request: Request) -> bool:
return request.cookies.get(SESSION_COOKIE) == os.getenv("VESTI_WEB_SESSION", "")
@@ -277,7 +292,7 @@ def _fetch_candidates(conn, direction: str, status: str, own: str, q: str, group
@app.get("/candidates", response_class=HTMLResponse)
def candidates(request: Request, direction: str = "", status: str = "", own: str = "",
q: str = "", group_by: str = "source", selected: int = 0, error: str = ""):
q: str = "", group_by: str = "date", selected: int = 0, error: str = ""):
_require_auth(request)
conn = _db()
grouped, counts, status = _fetch_candidates(conn, direction, status, own, q, group_by)
@@ -294,6 +309,11 @@ def candidates(request: Request, direction: str = "", status: str = "", own: str
else:
continue
break
# открытие поста = прочтение (как в почте): сразу помечаем, чтобы карточка перестала быть жирной
if sel is not None and sel["id"]:
conn.execute("UPDATE posts SET is_read=1 WHERE id=? AND is_read=0", (sel["id"],))
sel["is_read"] = 1
conn.commit()
conn.close()
# строка параметров фильтров для ссылок (выбор поста, смена группировки)
qs = []
@@ -313,7 +333,7 @@ def candidates(request: Request, direction: str = "", status: str = "", own: str
@app.post("/candidates/select")
def candidates_select(request: Request, id: int = Form(...), direction: str = Form(""),
status: str = Form(""), own: str = Form(""), q: str = Form(""), group_by: str = Form("source")):
status: str = Form(""), own: str = Form(""), q: str = Form(""), group_by: str = Form("date")):
"""Выбор кандидата левой панели (обычная форма без JS)."""
_require_auth(request)
url = f"/candidates?selected={id}&group_by={group_by}"
@@ -326,7 +346,7 @@ def candidates_select(request: Request, id: int = Form(...), direction: str = Fo
@app.post("/candidates/bulk")
def candidates_bulk(request: Request, action: str = Form(...), ids: list[int] = Form(default_factory=list),
direction: str = Form(""), status: str = Form(""), own: str = Form(""),
q: str = Form(""), group_by: str = Form("source")):
q: str = Form(""), group_by: str = Form("date")):
"""Bulk-операции: approve/reject/reject-old/process по отмеченным ids."""
_require_auth(request)
back = f"/candidates?group_by={group_by}"