mirror of
https://gitverse.ru/kpa39l/vesti.git
synced 2026-09-29 09:55:03 +00:00
candidates UI: full width, action and bulk buttons
- base.html: container -> container-fluid (nav and content full width) - Right pane: large buttons Publish / Process with local model / Rewrite / Reject (for new candidates) - Left pane bottom: bulk operations panel with 4 visible buttons (Publish / Process / Reject / Stale) instead of hidden select - bulk: action 'process' reclassifies selected with local model (impl_reclassify extracted); ids optional (empty selection -> no_selection redirect instead of 422); redirect preserves filters - Tests: full width, buttons present, bulk empty/process, published/metrics — green
This commit is contained in:
+35
-13
@@ -280,13 +280,17 @@ 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(...),
|
||||
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")):
|
||||
"""Bulk-операции: approve/reject/reject-old по отмеченным ids."""
|
||||
"""Bulk-операции: approve/reject/reject-old/process по отмеченным ids."""
|
||||
_require_auth(request)
|
||||
back = f"/candidates?group_by={group_by}"
|
||||
for k, v in (("direction", direction), ("status", status), ("own", own), ("q", q)):
|
||||
if v:
|
||||
back += f"&{k}={quote(v)}"
|
||||
if not ids:
|
||||
return RedirectResponse(url="/candidates?error=no_selection", status_code=302)
|
||||
return RedirectResponse(url=back + "&error=no_selection", status_code=302)
|
||||
conn = _db()
|
||||
ph = ",".join("?" * len(ids))
|
||||
rows = conn.execute(f"SELECT * FROM posts WHERE id IN ({ph})", ids).fetchall()
|
||||
@@ -294,7 +298,7 @@ def candidates_bulk(request: Request, action: str = Form(...), ids: list[int] =
|
||||
conn.execute(f"UPDATE posts SET status='rejected' WHERE id IN ({ph})", ids)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return RedirectResponse(url=f"/candidates?status=rejected&group_by={group_by}", status_code=302)
|
||||
return RedirectResponse(url=back + "&status=rejected", status_code=302)
|
||||
if action == "reject-old":
|
||||
# все выбранные, кроме самых свежих (по 1 от источника) — «почистить старьё»
|
||||
keep = {}
|
||||
@@ -307,9 +311,22 @@ def candidates_bulk(request: Request, action: str = Form(...), ids: list[int] =
|
||||
conn.execute(f"UPDATE posts SET status='rejected' WHERE id IN ({ph2})", kill)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return RedirectResponse(url=f"/candidates?status=rejected&group_by={group_by}", status_code=302)
|
||||
# approve — каждый через существующую логику (публикация + бандл)
|
||||
return RedirectResponse(url=back + "&status=rejected", status_code=302)
|
||||
conn.close()
|
||||
if action == "process":
|
||||
# обработка локальной моделью (переклассификация) по выбранным
|
||||
errs = []
|
||||
done = 0
|
||||
for r in rows:
|
||||
try:
|
||||
impl_reclassify(int(r["id"]))
|
||||
done += 1
|
||||
except Exception as e:
|
||||
errs.append(f"{r['id']}({e})")
|
||||
if errs:
|
||||
return RedirectResponse(url=f"/candidates?error=process_partial:{','.join(map(str,errs))[:120]}&group_by={group_by}", status_code=302)
|
||||
return RedirectResponse(url=back + "&error=processed:" + str(done), status_code=302)
|
||||
# approve — каждый через существующую логику (публикация + бандл)
|
||||
errs = []
|
||||
for r in rows:
|
||||
if r["status"] == "published":
|
||||
@@ -334,25 +351,30 @@ def post_comment(post_id: int, request: Request, comment: str = Form("")):
|
||||
|
||||
@app.post("/posts/{post_id}/reclassify")
|
||||
def post_reclassify(post_id: int, request: Request):
|
||||
"""Переклассификация кандидата локальной моделью (qwen)."""
|
||||
_require_auth(request)
|
||||
try:
|
||||
impl_reclassify(post_id)
|
||||
except Exception as e:
|
||||
return RedirectResponse(url=f"/candidates?selected={post_id}&error=reclassify:{e}", status_code=302)
|
||||
return RedirectResponse(url=f"/candidates?selected={post_id}", status_code=302)
|
||||
|
||||
|
||||
def impl_reclassify(post_id: int):
|
||||
"""Перезапуск классификации локальной моделью. Бросает исключение при неудаче."""
|
||||
from classifier.classify import classify_text
|
||||
conn = _db()
|
||||
p = conn.execute("SELECT * FROM posts WHERE id=?", (post_id,)).fetchone()
|
||||
if not p:
|
||||
conn.close()
|
||||
return RedirectResponse(url="/candidates?error=notfound", status_code=302)
|
||||
try:
|
||||
res = classify_text(dict(p))
|
||||
except Exception as e:
|
||||
conn.close()
|
||||
return RedirectResponse(url=f"/candidates?selected={post_id}&error=reclassify:{e}", status_code=302)
|
||||
raise ValueError("notfound")
|
||||
res = classify_text(dict(p))
|
||||
conn.execute(
|
||||
"UPDATE posts SET direction=?, relevance=?, interest=?, summary=?, classified=? WHERE id=?",
|
||||
(res["direction"], res["relevance"], res["interest"], res["summary"], 1 if res["classified"] else 0, post_id),
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return RedirectResponse(url=f"/candidates?selected={post_id}", status_code=302)
|
||||
|
||||
|
||||
@app.post("/posts/{post_id}/rewrite")
|
||||
|
||||
Reference in New Issue
Block a user