464 lines
16 KiB
Python
464 lines
16 KiB
Python
from db import get_connection
|
||
from repositories.match_view_repository import get_match_lineups_grouped
|
||
from repositories.match_coach_repository import get_match_coaches_grouped
|
||
|
||
|
||
def replace_match_lineups(match_id: int, rows: list[dict]) -> None:
|
||
delete_query = "DELETE FROM match_lineups WHERE match_id = %s;"
|
||
insert_query = """
|
||
INSERT INTO match_lineups (
|
||
match_id,
|
||
team_id,
|
||
player_id,
|
||
player_name,
|
||
number,
|
||
position,
|
||
is_captain,
|
||
lineup_type,
|
||
source,
|
||
created_at,
|
||
updated_at
|
||
)
|
||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW());
|
||
"""
|
||
|
||
conn = get_connection()
|
||
try:
|
||
with conn.cursor() as cur:
|
||
cur.execute(delete_query, (match_id,))
|
||
for row in rows:
|
||
cur.execute(
|
||
insert_query,
|
||
(
|
||
row["match_id"],
|
||
row["team_id"],
|
||
row.get("player_id"),
|
||
row["player_name"],
|
||
row.get("number"),
|
||
row.get("position"),
|
||
row.get("is_captain"),
|
||
row["lineup_type"],
|
||
row.get("source", "parser"),
|
||
),
|
||
)
|
||
conn.commit()
|
||
except Exception:
|
||
conn.rollback()
|
||
raise
|
||
finally:
|
||
conn.close()
|
||
|
||
|
||
def _player_to_editor_dict(p: dict) -> dict:
|
||
return {
|
||
"player_id": p.get("player_id"),
|
||
"player_name": p.get("player_name")
|
||
or f"{p.get('last_name', '')} {p.get('first_name', '')}".strip(),
|
||
"last_name": p.get("last_name", "") or p.get("player_name", "") or "",
|
||
"first_name": p.get("first_name", "") or "",
|
||
"number": str(p.get("number", "") or ""),
|
||
"position": p.get("position", "") or "",
|
||
"position_full": p.get("position_full", "") or "",
|
||
"is_captain": bool(p.get("is_captain")),
|
||
}
|
||
|
||
|
||
def _coach_to_editor_dict(c: dict) -> dict:
|
||
return {
|
||
"coach_id": c.get("coach_id"),
|
||
"coach_name": c.get("coach_name", "") or "",
|
||
"role": c.get("role", "") or "",
|
||
}
|
||
|
||
|
||
def get_match_lineup_for_editor(
|
||
match_id: int,
|
||
home_team_id: int,
|
||
away_team_id: int,
|
||
) -> dict:
|
||
conn = get_connection()
|
||
try:
|
||
with conn.cursor() as cur:
|
||
cur.execute(
|
||
"""
|
||
SELECT
|
||
mlp.side,
|
||
mlp.role,
|
||
mlp.sort_order,
|
||
p.id AS player_id,
|
||
COALESCE(p.full_name, TRIM(COALESCE(p.last_name, '') || ' ' || COALESCE(p.first_name, ''))) AS player_name,
|
||
COALESCE(p.last_name, '') AS last_name,
|
||
COALESCE(p.first_name, '') AS first_name,
|
||
COALESCE(mlp.number::text, '') AS number,
|
||
COALESCE(mlp.position, '') AS position,
|
||
COALESCE(mlp.is_captain, FALSE) AS is_captain,
|
||
p.position as position_full
|
||
FROM match_lineup_players mlp
|
||
JOIN players p
|
||
ON p.id = mlp.player_id
|
||
WHERE mlp.match_id = %s
|
||
ORDER BY mlp.side, mlp.role, mlp.sort_order, mlp.id
|
||
""",
|
||
(match_id,),
|
||
)
|
||
player_rows = cur.fetchall()
|
||
|
||
cur.execute(
|
||
"""
|
||
SELECT
|
||
mc.side,
|
||
mc.sort_order,
|
||
c.id AS coach_id,
|
||
COALESCE(c.player, '') AS coach_name,
|
||
COALESCE(mc.role, c.amplua, '') AS role
|
||
FROM match_coaches mc
|
||
JOIN coaches c
|
||
ON c.id = mc.coach_id
|
||
WHERE mc.match_id = %s
|
||
ORDER BY mc.side, mc.sort_order, mc.id
|
||
""",
|
||
(match_id,),
|
||
)
|
||
coach_rows = cur.fetchall()
|
||
finally:
|
||
conn.close()
|
||
|
||
result = {
|
||
"home_starting": [],
|
||
"home_bench": [],
|
||
"away_starting": [],
|
||
"away_bench": [],
|
||
"home_coaches": [],
|
||
"away_coaches": [],
|
||
}
|
||
|
||
# Если редактор уже сохранял состав — берём игроков из новой таблицы
|
||
if player_rows:
|
||
for row in player_rows:
|
||
player = {
|
||
"player_id": row[3],
|
||
"player_name": row[4] or "",
|
||
"last_name": row[5] or "",
|
||
"first_name": row[6] or "",
|
||
"number": row[7] or "",
|
||
"position": row[8] or "",
|
||
"position_full": row[10] or "",
|
||
"is_captain": bool(row[9]),
|
||
}
|
||
|
||
side = row[0]
|
||
role = row[1]
|
||
|
||
if side == "home" and role == "starting":
|
||
result["home_starting"].append(player)
|
||
elif side == "home" and role == "bench":
|
||
result["home_bench"].append(player)
|
||
elif side == "away" and role == "starting":
|
||
result["away_starting"].append(player)
|
||
elif side == "away" and role == "bench":
|
||
result["away_bench"].append(player)
|
||
else:
|
||
# Иначе берём загруженный с сайта состав из старой таблицы
|
||
lineups = get_match_lineups_grouped(
|
||
match_id=match_id,
|
||
home_team_id=home_team_id,
|
||
away_team_id=away_team_id,
|
||
)
|
||
|
||
result["home_starting"] = [
|
||
_player_to_editor_dict(p) for p in lineups.get("home_starting", [])
|
||
]
|
||
result["home_bench"] = [
|
||
_player_to_editor_dict(p) for p in lineups.get("home_bench", [])
|
||
]
|
||
result["away_starting"] = [
|
||
_player_to_editor_dict(p) for p in lineups.get("away_starting", [])
|
||
]
|
||
result["away_bench"] = [
|
||
_player_to_editor_dict(p) for p in lineups.get("away_bench", [])
|
||
]
|
||
|
||
# Тренеров берём из match_coaches, если они уже есть
|
||
if coach_rows:
|
||
for row in coach_rows:
|
||
coach = {
|
||
"coach_id": row[2],
|
||
"coach_name": row[3] or "",
|
||
"role": row[4] or "",
|
||
}
|
||
|
||
side = row[0]
|
||
if side == "home":
|
||
result["home_coaches"].append(coach)
|
||
elif side == "away":
|
||
result["away_coaches"].append(coach)
|
||
else:
|
||
coaches = get_match_coaches_grouped(
|
||
match_id=match_id,
|
||
home_team_id=home_team_id,
|
||
away_team_id=away_team_id,
|
||
)
|
||
|
||
result["home_coaches"] = [
|
||
_coach_to_editor_dict(c) for c in coaches.get("home", [])
|
||
]
|
||
result["away_coaches"] = [
|
||
_coach_to_editor_dict(c) for c in coaches.get("away", [])
|
||
]
|
||
|
||
return result
|
||
|
||
def get_match_lineup_for_vmix(
|
||
match_id: int,
|
||
home_team_id: int,
|
||
away_team_id: int,
|
||
) -> dict:
|
||
conn = get_connection()
|
||
try:
|
||
with conn.cursor() as cur:
|
||
# Сначала пробуем взять ручную версию из редактора
|
||
cur.execute(
|
||
"""
|
||
SELECT
|
||
mlp.side,
|
||
mlp.role,
|
||
mlp.sort_order,
|
||
p.id AS player_id,
|
||
COALESCE(
|
||
p.full_name,
|
||
TRIM(COALESCE(p.last_name, '') || ' ' || COALESCE(p.first_name, ''))
|
||
) AS player_name,
|
||
COALESCE(p.last_name, '') AS last_name,
|
||
COALESCE(p.first_name, '') AS first_name,
|
||
COALESCE(mlp.number::text, '') AS number,
|
||
COALESCE(mlp.position, '') AS position,
|
||
COALESCE(mlp.is_captain, FALSE) AS is_captain,
|
||
COALESCE(p.position, '') AS pos
|
||
FROM match_lineup_players mlp
|
||
JOIN players p
|
||
ON p.id = mlp.player_id
|
||
WHERE mlp.match_id = %s
|
||
ORDER BY
|
||
CASE
|
||
WHEN LOWER(COALESCE(p.position, '')) IN ('вр', 'вр.', 'gk', 'goalkeeper', 'вратарь') THEN 0
|
||
ELSE 1
|
||
END,
|
||
CASE
|
||
WHEN COALESCE(p.number::text, '') ~ '^[0-9]+$' THEN p.number::integer
|
||
ELSE 999
|
||
END,
|
||
COALESCE(p.last_name, ''),
|
||
COALESCE(p.first_name, ''),
|
||
p.id
|
||
""",
|
||
(match_id,),
|
||
)
|
||
player_rows = cur.fetchall()
|
||
|
||
cur.execute(
|
||
"""
|
||
SELECT
|
||
mc.side,
|
||
mc.sort_order,
|
||
c.id AS coach_id,
|
||
COALESCE(c.player, '') AS coach_name,
|
||
COALESCE(mc.role, c.amplua, '') AS role
|
||
FROM match_coaches mc
|
||
JOIN coaches c
|
||
ON c.id = mc.coach_id
|
||
WHERE mc.match_id = %s
|
||
ORDER BY mc.side, mc.sort_order, mc.id
|
||
""",
|
||
(match_id,),
|
||
)
|
||
coach_rows = cur.fetchall()
|
||
finally:
|
||
conn.close()
|
||
|
||
result = {
|
||
"home_starting": [],
|
||
"home_bench": [],
|
||
"away_starting": [],
|
||
"away_bench": [],
|
||
"home_coaches": [],
|
||
"away_coaches": [],
|
||
}
|
||
|
||
# 1. Если есть ручной состав — берём его
|
||
if player_rows:
|
||
for row in player_rows:
|
||
player = {
|
||
"player_id": row[3],
|
||
"player_name": row[4] or "",
|
||
"last_name": row[5] or "",
|
||
"first_name": row[6] or "",
|
||
"number": row[7] or "",
|
||
"position": row[8] or "",
|
||
"is_captain": bool(row[9]),
|
||
"pos": row[10] or "",
|
||
}
|
||
|
||
side = row[0]
|
||
role = row[1]
|
||
|
||
if side == "home" and role == "starting":
|
||
result["home_starting"].append(player)
|
||
elif side == "home" and role == "bench":
|
||
result["home_bench"].append(player)
|
||
elif side == "away" and role == "starting":
|
||
result["away_starting"].append(player)
|
||
elif side == "away" and role == "bench":
|
||
result["away_bench"].append(player)
|
||
|
||
else:
|
||
# 2. Иначе — fallback на сайт
|
||
lineups = get_match_lineups_grouped(
|
||
match_id=match_id,
|
||
home_team_id=home_team_id,
|
||
away_team_id=away_team_id,
|
||
)
|
||
|
||
result["home_starting"] = lineups.get("home_starting", [])
|
||
result["home_bench"] = lineups.get("home_bench", [])
|
||
result["away_starting"] = lineups.get("away_starting", [])
|
||
result["away_bench"] = lineups.get("away_bench", [])
|
||
|
||
if coach_rows:
|
||
for row in coach_rows:
|
||
coach = {
|
||
"coach_id": row[2],
|
||
"coach_name": row[3] or "",
|
||
"role": row[4] or "",
|
||
}
|
||
|
||
if row[0] == "home":
|
||
result["home_coaches"].append(coach)
|
||
elif row[0] == "away":
|
||
result["away_coaches"].append(coach)
|
||
else:
|
||
coaches = get_match_coaches_grouped(
|
||
match_id=match_id,
|
||
home_team_id=home_team_id,
|
||
away_team_id=away_team_id,
|
||
)
|
||
result["home_coaches"] = coaches.get("home", [])
|
||
result["away_coaches"] = coaches.get("away", [])
|
||
|
||
return result
|
||
|
||
def save_match_lineup_for_editor(
|
||
match_id: int,
|
||
home_team_id: int,
|
||
away_team_id: int,
|
||
home_starting: list[dict],
|
||
home_bench: list[dict],
|
||
away_starting: list[dict],
|
||
away_bench: list[dict],
|
||
home_coaches: list[dict],
|
||
away_coaches: list[dict],
|
||
) -> None:
|
||
def is_goalkeeper(position: str) -> bool:
|
||
if not position:
|
||
return False
|
||
return str(position).strip().lower() in {
|
||
"вр", "вр.", "вратарь", "gk", "goalkeeper"
|
||
}
|
||
|
||
def safe_number(value) -> int:
|
||
try:
|
||
return int(str(value).strip())
|
||
except Exception:
|
||
return 9999
|
||
|
||
def lineup_sort_key(player: dict):
|
||
return (
|
||
0 if is_goalkeeper(player.get("position", "")) else 1,
|
||
safe_number(player.get("number")),
|
||
(player.get("last_name") or player.get("player_name") or "").lower(),
|
||
(player.get("first_name") or "").lower(),
|
||
player.get("player_id") or 0,
|
||
)
|
||
|
||
# Пересортировка перед сохранением
|
||
home_starting = sorted(home_starting, key=lineup_sort_key)
|
||
home_bench = sorted(home_bench, key=lineup_sort_key)
|
||
away_starting = sorted(away_starting, key=lineup_sort_key)
|
||
away_bench = sorted(away_bench, key=lineup_sort_key)
|
||
|
||
conn = get_connection()
|
||
try:
|
||
with conn.cursor() as cur:
|
||
cur.execute(
|
||
"DELETE FROM match_lineup_players WHERE match_id = %s",
|
||
(match_id,),
|
||
)
|
||
cur.execute(
|
||
"DELETE FROM match_coaches WHERE match_id = %s",
|
||
(match_id,),
|
||
)
|
||
|
||
def insert_players(side: str, role: str, players: list[dict]) -> None:
|
||
for sort_order, p in enumerate(players, start=1):
|
||
cur.execute(
|
||
"""
|
||
INSERT INTO match_lineup_players (
|
||
match_id,
|
||
side,
|
||
role,
|
||
sort_order,
|
||
player_id,
|
||
number,
|
||
position,
|
||
is_captain
|
||
)
|
||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
|
||
""",
|
||
(
|
||
match_id,
|
||
side,
|
||
role,
|
||
sort_order,
|
||
p.get("player_id"),
|
||
p.get("number", "") or None,
|
||
p.get("position", "") or None,
|
||
bool(p.get("is_captain")),
|
||
),
|
||
)
|
||
|
||
def insert_coaches(side: str, coaches: list[dict]) -> None:
|
||
for sort_order, c in enumerate(coaches, start=1):
|
||
cur.execute(
|
||
"""
|
||
INSERT INTO match_coaches (
|
||
match_id,
|
||
side,
|
||
sort_order,
|
||
coach_id,
|
||
role
|
||
)
|
||
VALUES (%s, %s, %s, %s, %s)
|
||
""",
|
||
(
|
||
match_id,
|
||
side,
|
||
sort_order,
|
||
c.get("coach_id"),
|
||
c.get("role", "") or None,
|
||
),
|
||
)
|
||
|
||
insert_players("home", "starting", home_starting)
|
||
insert_players("home", "bench", home_bench)
|
||
insert_players("away", "starting", away_starting)
|
||
insert_players("away", "bench", away_bench)
|
||
|
||
insert_coaches("home", home_coaches)
|
||
insert_coaches("away", away_coaches)
|
||
|
||
conn.commit()
|
||
except Exception:
|
||
conn.rollback()
|
||
raise
|
||
finally:
|
||
conn.close()
|