добавил кнопки парсинга в редактировании базы для админов
This commit is contained in:
60
app.py
60
app.py
@@ -16,6 +16,8 @@ import json
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import io
|
import io
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import contextlib
|
||||||
|
import traceback
|
||||||
import time
|
import time
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
|
|
||||||
@@ -26,6 +28,9 @@ from repositories.match_formation_repository import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from parsers.parser_game import run_parser_game
|
from parsers.parser_game import run_parser_game
|
||||||
|
from parsers.parser_players import run_parser_players
|
||||||
|
from parsers.parser_schedule import run_parser_schedule
|
||||||
|
from parsers.parser_standings import run_parser_standings
|
||||||
from repositories.match_session_repository import (
|
from repositories.match_session_repository import (
|
||||||
create_match_session,
|
create_match_session,
|
||||||
get_match_session_by_token,
|
get_match_session_by_token,
|
||||||
@@ -725,17 +730,68 @@ def close_session(session_token: str):
|
|||||||
return RedirectResponse(url="/admin/matches", status_code=303)
|
return RedirectResponse(url="/admin/matches", status_code=303)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/admin/db", response_class=HTMLResponse)
|
def render_admin_db_index(request: Request, parser_output: str | None = None, parser_name: str | None = None, parser_success: bool | None = None):
|
||||||
def admin_db_index(request: Request):
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
name="admin_db_index.html",
|
name="admin_db_index.html",
|
||||||
request=request,
|
request=request,
|
||||||
context={
|
context={
|
||||||
"current_user": getattr(request.state, "current_user", None),
|
"current_user": getattr(request.state, "current_user", None),
|
||||||
|
"parser_output": parser_output,
|
||||||
|
"parser_name": parser_name,
|
||||||
|
"parser_success": parser_success,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/admin/db", response_class=HTMLResponse)
|
||||||
|
def admin_db_index(request: Request):
|
||||||
|
return render_admin_db_index(request)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/admin/db/run-parser", response_class=HTMLResponse)
|
||||||
|
def admin_db_run_parser(request: Request, parser_name: str = Form(...)):
|
||||||
|
current_user = getattr(request.state, "current_user", None)
|
||||||
|
if not current_user or current_user.get("role") != "admin":
|
||||||
|
return RedirectResponse(url="/login", status_code=303)
|
||||||
|
|
||||||
|
parser_map = {
|
||||||
|
"players": ("Игроки", run_parser_players),
|
||||||
|
"schedule": ("Расписание", run_parser_schedule),
|
||||||
|
"standings": ("Турнирка", run_parser_standings),
|
||||||
|
}
|
||||||
|
|
||||||
|
parser_meta = parser_map.get(parser_name)
|
||||||
|
if not parser_meta:
|
||||||
|
return render_admin_db_index(
|
||||||
|
request,
|
||||||
|
parser_output="Неизвестный парсер.",
|
||||||
|
parser_name=parser_name,
|
||||||
|
parser_success=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
title, parser_func = parser_meta
|
||||||
|
buffer = io.StringIO()
|
||||||
|
success = True
|
||||||
|
|
||||||
|
with contextlib.redirect_stdout(buffer), contextlib.redirect_stderr(buffer):
|
||||||
|
print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Запуск парсера: {title}")
|
||||||
|
try:
|
||||||
|
parser_func()
|
||||||
|
print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Парсер завершён успешно")
|
||||||
|
except Exception:
|
||||||
|
success = False
|
||||||
|
print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Ошибка при выполнении парсера")
|
||||||
|
print(traceback.format_exc())
|
||||||
|
|
||||||
|
output = buffer.getvalue().strip() or "Парсер не вернул сообщений."
|
||||||
|
return render_admin_db_index(
|
||||||
|
request,
|
||||||
|
parser_output=output,
|
||||||
|
parser_name=title,
|
||||||
|
parser_success=success,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/admin/db/players", response_class=HTMLResponse)
|
@app.get("/admin/db/players", response_class=HTMLResponse)
|
||||||
def admin_db_players(request: Request, q: str = Query(default="")):
|
def admin_db_players(request: Request, q: str = Query(default="")):
|
||||||
# denied = require_role(request, {"admin"})
|
# denied = require_role(request, {"admin"})
|
||||||
|
|||||||
@@ -13,6 +13,10 @@
|
|||||||
--muted: #9aa4b2;
|
--muted: #9aa4b2;
|
||||||
--accent: #4f8cff;
|
--accent: #4f8cff;
|
||||||
--accent-hover: #3e78e6;
|
--accent-hover: #3e78e6;
|
||||||
|
--success-bg: rgba(46, 204, 113, 0.12);
|
||||||
|
--success-border: rgba(46, 204, 113, 0.35);
|
||||||
|
--danger-bg: rgba(255, 99, 99, 0.12);
|
||||||
|
--danger-border: rgba(255, 99, 99, 0.35);
|
||||||
--shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
|
--shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
|
||||||
--radius: 16px;
|
--radius: 16px;
|
||||||
}
|
}
|
||||||
@@ -46,15 +50,24 @@
|
|||||||
margin-bottom: 18px;
|
margin-bottom: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs {
|
.tabs,
|
||||||
|
.parser-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-link {
|
.parser-actions {
|
||||||
|
margin-top: 18px;
|
||||||
|
padding-top: 18px;
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-link,
|
||||||
|
.action-btn {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
min-height: 42px;
|
min-height: 42px;
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
@@ -63,13 +76,58 @@
|
|||||||
color: var(--text);
|
color: var(--text);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-link:hover {
|
.tab-link:hover,
|
||||||
|
.action-btn:hover {
|
||||||
background: var(--accent);
|
background: var(--accent);
|
||||||
border-color: var(--accent);
|
border-color: var(--accent);
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.action-form {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-block {
|
||||||
|
margin-top: 20px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 16px;
|
||||||
|
background: var(--panel-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-block.success {
|
||||||
|
background: var(--success-bg);
|
||||||
|
border-color: var(--success-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-block.error {
|
||||||
|
background: var(--danger-bg);
|
||||||
|
border-color: var(--danger-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-status {
|
||||||
|
color: var(--muted);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-output {
|
||||||
|
margin: 0;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
font-family: Consolas, Monaco, monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -79,14 +137,45 @@
|
|||||||
|
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<a class="tab-link" href="/admin/db/players">Игроки</a>
|
<a class="tab-link" href="/admin/db/players">Игроки</a>
|
||||||
|
|
||||||
<a class="tab-link" href="/admin/db/referees">Судьи</a>
|
<a class="tab-link" href="/admin/db/referees">Судьи</a>
|
||||||
<a class="tab-link" href="/admin/db/teams">Команды</a>
|
<a class="tab-link" href="/admin/db/teams">Команды</a>
|
||||||
<a class="tab-link" href="/admin/db/coaches">Тренеры</a>
|
<a class="tab-link" href="/admin/db/coaches">Тренеры</a>
|
||||||
<a class="tab-link" href="/admin/db/stadiums">Стадионы</a>
|
<a class="tab-link" href="/admin/db/stadiums">Стадионы</a>
|
||||||
<a class="tab-link" href="/admin/matches">Назад к матчам</a>
|
<a class="tab-link" href="/admin/matches">Назад к матчам</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="parser-actions">
|
||||||
|
<form method="post" action="/admin/db/run-parser" class="action-form">
|
||||||
|
<input type="hidden" name="parser_name" value="players">
|
||||||
|
<button type="submit" class="action-btn">Заграбить игроков</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<form method="post" action="/admin/db/run-parser" class="action-form">
|
||||||
|
<input type="hidden" name="parser_name" value="schedule">
|
||||||
|
<button type="submit" class="action-btn">Заграбить расписание</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<form method="post" action="/admin/db/run-parser" class="action-form">
|
||||||
|
<input type="hidden" name="parser_name" value="standings">
|
||||||
|
<button type="submit" class="action-btn">Заграбить турнирку</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if parser_output %}
|
||||||
|
<div class="log-block {% if parser_success %}success{% else %}error{% endif %}">
|
||||||
|
<div class="log-title">Итог парсинга{% if parser_name %}: {{ parser_name }}{% endif %}</div>
|
||||||
|
<div class="log-status">
|
||||||
|
Статус:
|
||||||
|
{% if parser_success %}
|
||||||
|
успешно
|
||||||
|
{% else %}
|
||||||
|
ошибка
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<pre class="log-output">{{ parser_output }}</pre>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user