826 lines
29 KiB
Python
826 lines
29 KiB
Python
# services/vmix_json_service.py
|
||
from db import get_connection
|
||
from repositories.match_lineup_repository import get_match_lineup_for_vmix
|
||
from repositories.match_repository import resolve_match_source_key
|
||
from parsers.parser_sources import build_empty_photo_path, build_logo_path, build_logo_variant_path, build_photo_path
|
||
|
||
|
||
DEFAULT_PHOTO_BASE_PATH = r"D:\Графика\ФУТБОЛ\Женская Суперлига 2026\Photo"
|
||
EMPTY_PHOTO_PATH = DEFAULT_PHOTO_BASE_PATH + r"\EMPTY.png"
|
||
|
||
|
||
def _session_source_key(session_row) -> str | None:
|
||
try:
|
||
match_id = session_row[1]
|
||
except Exception:
|
||
match_id = None
|
||
|
||
try:
|
||
value = session_row[21]
|
||
except Exception:
|
||
value = None
|
||
|
||
return resolve_match_source_key(match_id, value)
|
||
|
||
|
||
def build_generated_player_photo_path(
|
||
team_name: str,
|
||
last_name: str,
|
||
first_name: str,
|
||
source_key: str | None = None,
|
||
) -> str:
|
||
relative_file = (
|
||
str(team_name or "").strip()
|
||
+ "\\"
|
||
+ (str(last_name or "") + " " + str(first_name or "")).strip()
|
||
+ ".png"
|
||
)
|
||
return build_photo_path(source_key, relative_file)
|
||
|
||
|
||
def resolve_player_photo(
|
||
photo_enabled: bool,
|
||
generated_photo: str,
|
||
source_key: str | None = None,
|
||
) -> str:
|
||
if photo_enabled:
|
||
return generated_photo
|
||
return build_empty_photo_path(source_key) or EMPTY_PHOTO_PATH
|
||
|
||
|
||
def _normalize_text(value) -> str:
|
||
return " ".join(str(value or "").strip().lower().split())
|
||
|
||
|
||
def _player_name_variants(player: dict) -> set[str]:
|
||
first_name = _normalize_text(player.get("first_name"))
|
||
last_name = _normalize_text(player.get("last_name"))
|
||
player_name = _normalize_text(player.get("player_name"))
|
||
full_name = _normalize_text(player.get("full_name"))
|
||
|
||
variants = {player_name, full_name}
|
||
if first_name or last_name:
|
||
variants.add(f"{last_name} {first_name}".strip())
|
||
variants.add(f"{first_name} {last_name}".strip())
|
||
|
||
return {v for v in variants if v}
|
||
|
||
|
||
def _candidate_name_variants(row: tuple) -> set[str]:
|
||
_, full_name, last_name, first_name, *_ = row
|
||
full_name = _normalize_text(full_name)
|
||
last_name = _normalize_text(last_name)
|
||
first_name = _normalize_text(first_name)
|
||
|
||
variants = {full_name}
|
||
if first_name or last_name:
|
||
variants.add(f"{last_name} {first_name}".strip())
|
||
variants.add(f"{first_name} {last_name}".strip())
|
||
|
||
return {v for v in variants if v}
|
||
|
||
|
||
def _find_player_photo_state(
|
||
*,
|
||
player_id=None,
|
||
team_id=None,
|
||
player_name: str = "",
|
||
first_name: str = "",
|
||
last_name: str = "",
|
||
full_name: str = "",
|
||
number: str = "",
|
||
) -> tuple[str | None, bool] | None:
|
||
"""
|
||
Берёт актуальное состояние галочки photo_enabled прямо из players.
|
||
Если в составе нет корректного player_id, пробует найти игрока по команде, номеру и имени.
|
||
Это важно для старых/загруженных с сайта составов, где player_id мог быть пустым.
|
||
Значение p.photo здесь не используется для JSON-пути: путь генерируется по старой схеме.
|
||
"""
|
||
try:
|
||
player_id_int = int(player_id) if str(player_id or "").strip().isdigit() else None
|
||
except Exception:
|
||
player_id_int = None
|
||
|
||
try:
|
||
team_id_int = int(team_id) if str(team_id or "").strip().isdigit() else None
|
||
except Exception:
|
||
team_id_int = None
|
||
|
||
target_number = str(number or "").strip()
|
||
target_player = {
|
||
"player_name": player_name,
|
||
"first_name": first_name,
|
||
"last_name": last_name,
|
||
"full_name": full_name,
|
||
}
|
||
target_names = _player_name_variants(target_player)
|
||
|
||
where_parts = []
|
||
params = []
|
||
|
||
if player_id_int is not None:
|
||
where_parts.append("p.id = %s")
|
||
params.append(player_id_int)
|
||
|
||
if team_id_int is not None:
|
||
team_conditions = []
|
||
team_params = []
|
||
|
||
if target_number:
|
||
team_conditions.append("COALESCE(p.number::text, '') = %s")
|
||
team_params.append(target_number)
|
||
|
||
for name in sorted(target_names):
|
||
like = f"%{name}%"
|
||
team_conditions.append(
|
||
"""
|
||
(
|
||
LOWER(COALESCE(p.full_name, '')) = %s
|
||
OR LOWER(TRIM(COALESCE(p.last_name, '') || ' ' || COALESCE(p.first_name, ''))) = %s
|
||
OR LOWER(TRIM(COALESCE(p.first_name, '') || ' ' || COALESCE(p.last_name, ''))) = %s
|
||
OR LOWER(COALESCE(p.full_name, '')) LIKE %s
|
||
)
|
||
"""
|
||
)
|
||
team_params.extend([name, name, name, like])
|
||
|
||
if team_conditions:
|
||
where_parts.append(f"(p.team_id = %s AND ({' OR '.join(team_conditions)}))")
|
||
params.append(team_id_int)
|
||
params.extend(team_params)
|
||
|
||
if not where_parts:
|
||
return None
|
||
|
||
query = f"""
|
||
SELECT
|
||
p.id,
|
||
COALESCE(p.full_name, ''),
|
||
COALESCE(p.last_name, ''),
|
||
COALESCE(p.first_name, ''),
|
||
COALESCE(p.number::text, ''),
|
||
p.team_id,
|
||
p.photo,
|
||
COALESCE(p.photo_enabled, FALSE) AS photo_enabled
|
||
FROM players p
|
||
WHERE {' OR '.join(where_parts)}
|
||
LIMIT 50
|
||
"""
|
||
|
||
conn = get_connection()
|
||
try:
|
||
with conn.cursor() as cur:
|
||
cur.execute(query, tuple(params))
|
||
rows = cur.fetchall()
|
||
finally:
|
||
conn.close()
|
||
|
||
best_row = None
|
||
best_score = -1
|
||
|
||
for row in rows:
|
||
row_id, _, _, _, row_number, row_team_id, *_ = row
|
||
row_names = _candidate_name_variants(row)
|
||
id_match = player_id_int is not None and int(row_id) == player_id_int
|
||
team_match = team_id_int is not None and int(row_team_id) == team_id_int
|
||
number_match = bool(target_number) and str(row_number or "").strip() == target_number
|
||
name_match = bool(target_names.intersection(row_names))
|
||
|
||
partial_name_match = False
|
||
if target_names and row_names:
|
||
partial_name_match = any(
|
||
target in candidate or candidate in target
|
||
for target in target_names
|
||
for candidate in row_names
|
||
if len(target) >= 4 and len(candidate) >= 4
|
||
)
|
||
|
||
# Если ID выглядит как номер игрока и случайно совпал с чужим players.id,
|
||
# не считаем его надёжным без совпадения команды/имени/номера.
|
||
trusted_id_match = id_match and (
|
||
(team_id_int is None or team_match)
|
||
and (not target_number and not target_names or number_match or name_match or partial_name_match)
|
||
)
|
||
|
||
score = 0
|
||
if trusted_id_match:
|
||
score += 100
|
||
if team_match:
|
||
score += 40
|
||
if number_match:
|
||
score += 30
|
||
if name_match:
|
||
score += 35
|
||
elif partial_name_match:
|
||
score += 15
|
||
|
||
# Для поиска без надёжного ID нужно хотя бы совпадение команды и номера/имени.
|
||
if not trusted_id_match and not (team_match and (number_match or name_match or partial_name_match)):
|
||
continue
|
||
|
||
if score > best_score:
|
||
best_score = score
|
||
best_row = row
|
||
|
||
if not best_row:
|
||
return None
|
||
|
||
return best_row[6], bool(best_row[7])
|
||
|
||
|
||
def resolve_player_photo_for_json(
|
||
player: dict,
|
||
generated_photo: str,
|
||
fallback_team_id=None,
|
||
source_key: str | None = None,
|
||
) -> str:
|
||
state = _find_player_photo_state(
|
||
player_id=player.get("player_id") or player.get("id"),
|
||
team_id=player.get("team_id") or fallback_team_id,
|
||
player_name=player.get("player_name") or "",
|
||
first_name=player.get("first_name") or "",
|
||
last_name=player.get("last_name") or "",
|
||
full_name=player.get("full_name") or "",
|
||
number=player.get("number") or player.get("player_number") or "",
|
||
)
|
||
|
||
if state is not None:
|
||
photo_value, photo_enabled = state
|
||
resolved_photo = build_photo_path(source_key, photo_value) if photo_value else generated_photo
|
||
return resolve_player_photo(
|
||
photo_enabled=photo_enabled,
|
||
generated_photo=resolved_photo,
|
||
source_key=source_key,
|
||
)
|
||
|
||
photo_value = player.get("photo") or ""
|
||
resolved_photo = build_photo_path(source_key, photo_value) if photo_value else generated_photo
|
||
return resolve_player_photo(
|
||
photo_enabled=player.get("photo_enabled"),
|
||
generated_photo=resolved_photo,
|
||
source_key=source_key,
|
||
)
|
||
|
||
|
||
def build_lineup_json(match_id, home_team_id, away_team_id, name, team_a_name, team_b_name, source_key=None):
|
||
lineups = get_match_lineup_for_vmix(
|
||
match_id=match_id,
|
||
home_team_id=home_team_id,
|
||
away_team_id=away_team_id,
|
||
)
|
||
|
||
players = lineups.get(name, [])
|
||
|
||
result = []
|
||
|
||
fallback_team_id = home_team_id if str(name).startswith("home") else away_team_id
|
||
|
||
for p in players:
|
||
suffix = []
|
||
if "вратарь" in (p.get("pos") or "").lower():
|
||
suffix.append("ВР")
|
||
|
||
if p.get("is_captain"):
|
||
suffix.append("К")
|
||
number = p.get("number", "")
|
||
lastname = p.get("last_name", "")
|
||
suffix_str = f'({", ".join(suffix)})' if suffix else ""
|
||
result.append(
|
||
{
|
||
"number": p.get("number", ""),
|
||
"number_lastname_amp_K": f"{number} {lastname} {suffix_str}".strip(),
|
||
"first_name": p.get("first_name", ""),
|
||
"last_name": p.get("last_name", ""),
|
||
"number_fullname": f"{number} {p.get('first_name', '')} {p.get('last_name', '')}".strip(),
|
||
"full_name": (
|
||
p.get("first_name", "") + " " + p.get("last_name", "")
|
||
).strip(),
|
||
"full_name_K": (
|
||
p.get("first_name", "") + " " + p.get("last_name", "")
|
||
).strip()
|
||
+ (f" {', '.join(suffix)}" if suffix else ""),
|
||
"pos": p.get("pos", ""),
|
||
"position": p.get("position", ""),
|
||
"photo": resolve_player_photo_for_json(
|
||
p,
|
||
generated_photo=build_generated_player_photo_path(
|
||
team_name=team_a_name if "home" in name else team_b_name,
|
||
last_name=p.get("last_name", ""),
|
||
first_name=p.get("first_name", ""),
|
||
source_key=source_key,
|
||
),
|
||
fallback_team_id=fallback_team_id,
|
||
source_key=source_key,
|
||
),
|
||
}
|
||
)
|
||
|
||
return {"players": result}
|
||
|
||
|
||
def get_vmix_match_info_by_token(session_token: str):
|
||
query = """
|
||
SELECT
|
||
ms.match_id,
|
||
m.match_date,
|
||
m.place,
|
||
m.stadium_id,
|
||
ht.id AS home_team_id,
|
||
ht.name AS home_team_name,
|
||
|
||
COALESCE(ht.full_name, ht.name) AS home_team_full_name,
|
||
COALESCE(ht.short_name_3, '') AS home_team_short_name,
|
||
|
||
at.id AS away_team_id,
|
||
at.name AS away_team_name,
|
||
|
||
COALESCE(at.full_name, at.name) AS away_team_full_name,
|
||
COALESCE(at.short_name_3, '') AS away_team_short_name,
|
||
COALESCE(s.stadium_gfx, s.name, m.place, '') AS stadium_name,
|
||
|
||
m.tour,
|
||
ht.logo_path AS home_logo,
|
||
at.logo_path AS away_logo,
|
||
ref1.referee_name AS referee1,
|
||
ref2.referee_name AS referee2,
|
||
ref3.referee_name AS referee3,
|
||
ref4.referee_name AS referee4,
|
||
|
||
ht.logo_path AS home_logo1,
|
||
at.logo_path AS away_logo1,
|
||
ht.logo_path AS home_logo2,
|
||
at.logo_path AS away_logo2,
|
||
|
||
ht.city AS home_city,
|
||
at.city AS away_city,
|
||
|
||
TRIM(COALESCE(c1.name, '') || ' ' || COALESCE(c1.lastname, '')) AS coach_name1,
|
||
c1.amplua AS coach_amplua1,
|
||
|
||
TRIM(COALESCE(c2.name, '') || ' ' || COALESCE(c2.lastname, '')) AS coach_name2,
|
||
c2.amplua AS coach_amplua2,
|
||
m.source_key AS source_key
|
||
|
||
FROM match_sessions ms
|
||
JOIN matches m ON m.id = ms.match_id
|
||
JOIN teams ht ON ht.id = m.home_team_id
|
||
JOIN teams at ON at.id = m.away_team_id
|
||
|
||
LEFT JOIN match_referees ref1 ON ref1.match_id = m.id AND ref1.role = 'Главный судья'
|
||
LEFT JOIN match_referees ref2 ON ref2.match_id = m.id AND ref2.role = 'Ассистент судьи №1'
|
||
LEFT JOIN match_referees ref3 ON ref3.match_id = m.id AND ref3.role = 'Ассистент судьи №2'
|
||
LEFT JOIN match_referees ref4 ON ref4.match_id = m.id AND ref4.role = 'Резервный судья'
|
||
|
||
LEFT JOIN match_coaches mc1 ON mc1.match_id = m.id AND mc1.side = 'home'
|
||
LEFT JOIN coaches c1 ON c1.id = mc1.coach_id
|
||
|
||
LEFT JOIN match_coaches mc2 ON mc2.match_id = m.id AND mc2.side = 'away'
|
||
LEFT JOIN coaches c2 ON c2.id = mc2.coach_id
|
||
|
||
LEFT JOIN stadiums s ON s.id = m.stadium_id
|
||
|
||
WHERE ms.session_token = %s
|
||
LIMIT 1;
|
||
"""
|
||
|
||
conn = get_connection()
|
||
try:
|
||
with conn.cursor() as cur:
|
||
cur.execute(query, (session_token,))
|
||
row = cur.fetchone()
|
||
|
||
if not row:
|
||
return None
|
||
|
||
row = list(row)
|
||
source_key = resolve_match_source_key(row[0], row[30] if len(row) > 30 else None)
|
||
if len(row) > 30:
|
||
row[30] = source_key
|
||
row[14] = build_logo_path(source_key, row[14])
|
||
row[15] = build_logo_path(source_key, row[15])
|
||
row[20] = build_logo_variant_path(source_key, row[20], "white")
|
||
row[21] = build_logo_variant_path(source_key, row[21], "white")
|
||
row[22] = build_logo_variant_path(source_key, row[22], "blue")
|
||
row[23] = build_logo_variant_path(source_key, row[23], "blue")
|
||
return tuple(row)
|
||
finally:
|
||
conn.close()
|
||
|
||
|
||
def get_vmix_standings(session_row):
|
||
source_key = _session_source_key(session_row)
|
||
season = session_row[11] if len(session_row) > 11 else None
|
||
|
||
query = """
|
||
SELECT
|
||
s.position,
|
||
COALESCE(t.full_name, t.name) AS team_name,
|
||
t.logo_path AS logo,
|
||
s.played,
|
||
s.wins,
|
||
s.losses,
|
||
s.draws,
|
||
s.points_for || ' - ' || s.points_against AS score,
|
||
s.points,
|
||
s.team_id
|
||
FROM standings s
|
||
LEFT JOIN teams t ON s.team_id = t.id
|
||
WHERE (%s IS NULL OR s.season = %s)
|
||
ORDER BY s.position
|
||
"""
|
||
|
||
conn = get_connection()
|
||
try:
|
||
with conn.cursor() as cur:
|
||
cur.execute(query, (season, season))
|
||
rows = cur.fetchall()
|
||
|
||
result = []
|
||
for row in rows:
|
||
row = list(row)
|
||
row[2] = build_logo_variant_path(source_key, row[2], "white")
|
||
result.append(tuple(row))
|
||
return result
|
||
finally:
|
||
conn.close()
|
||
|
||
|
||
def get_vmix_schedule(session_row):
|
||
source_key = _session_source_key(session_row)
|
||
tour = session_row[10] if len(session_row) > 10 else None
|
||
season = session_row[11] if len(session_row) > 11 else None
|
||
source_filter = source_key or "SUPERLEAGUE"
|
||
|
||
query = """
|
||
SELECT
|
||
t1.logo_path AS logo1,
|
||
t2.logo_path AS logo2,
|
||
m.home_score,
|
||
m.away_score,
|
||
m.match_date,
|
||
m.status,
|
||
m.id,
|
||
m.channel
|
||
FROM matches m
|
||
LEFT JOIN teams t1 ON m.home_team_id = t1.id
|
||
LEFT JOIN teams t2 ON m.away_team_id = t2.id
|
||
WHERE m.tour = %s
|
||
AND (%s IS NULL OR m.season = %s)
|
||
AND COALESCE(m.source_key, %s) = %s
|
||
ORDER BY m.match_date, m.id
|
||
"""
|
||
|
||
conn = get_connection()
|
||
try:
|
||
with conn.cursor() as cur:
|
||
cur.execute(query, (tour, season, season, source_filter, source_filter))
|
||
rows = cur.fetchall()
|
||
|
||
result = []
|
||
for row in rows:
|
||
row = list(row)
|
||
row[0] = build_logo_variant_path(source_key, row[0], "white")
|
||
row[1] = build_logo_variant_path(source_key, row[1], "white")
|
||
result.append(tuple(row))
|
||
return result
|
||
finally:
|
||
conn.close()
|
||
|
||
|
||
def get_vmix_team_formations(session_token: str, team_id: int):
|
||
query = """
|
||
SELECT
|
||
COALESCE(NULLIF(p.last_name, ''), NULLIF(mf.player_name, ''), '') AS last_name,
|
||
mf.is_captain,
|
||
COALESCE(NULLIF(mf.number, ''), p.number::text, '') AS number,
|
||
COALESCE(NULLIF(mf.position, ''), p.position, '') AS position,
|
||
COALESCE(p.first_name, '') AS first_name,
|
||
p.photo,
|
||
COALESCE(p.photo_enabled, FALSE) AS photo_enabled,
|
||
mf.player_id,
|
||
mf.team_id,
|
||
COALESCE(mf.player_name, '') AS player_name
|
||
FROM match_formations mf
|
||
LEFT JOIN LATERAL (
|
||
SELECT p.*
|
||
FROM players p
|
||
WHERE p.team_id = mf.team_id
|
||
AND (
|
||
p.id = mf.player_id
|
||
OR (
|
||
COALESCE(p.number::text, '') <> ''
|
||
AND COALESCE(p.number::text, '') = COALESCE(mf.number, '')
|
||
)
|
||
OR (
|
||
COALESCE(mf.player_name, '') <> ''
|
||
AND (
|
||
LOWER(COALESCE(p.full_name, '')) = LOWER(TRIM(mf.player_name))
|
||
OR LOWER(TRIM(COALESCE(p.last_name, '') || ' ' || COALESCE(p.first_name, ''))) = LOWER(TRIM(mf.player_name))
|
||
OR LOWER(TRIM(COALESCE(p.first_name, '') || ' ' || COALESCE(p.last_name, ''))) = LOWER(TRIM(mf.player_name))
|
||
)
|
||
)
|
||
)
|
||
ORDER BY
|
||
CASE
|
||
WHEN p.id = mf.player_id
|
||
AND (
|
||
COALESCE(mf.number, '') = ''
|
||
OR COALESCE(p.number::text, '') = COALESCE(mf.number, '')
|
||
OR LOWER(COALESCE(p.full_name, '')) = LOWER(TRIM(COALESCE(mf.player_name, '')))
|
||
OR LOWER(TRIM(COALESCE(p.last_name, '') || ' ' || COALESCE(p.first_name, ''))) = LOWER(TRIM(COALESCE(mf.player_name, '')))
|
||
OR LOWER(TRIM(COALESCE(p.first_name, '') || ' ' || COALESCE(p.last_name, ''))) = LOWER(TRIM(COALESCE(mf.player_name, '')))
|
||
)
|
||
THEN 0
|
||
WHEN COALESCE(p.number::text, '') = COALESCE(mf.number, '')
|
||
AND COALESCE(mf.player_name, '') <> ''
|
||
THEN 1
|
||
WHEN COALESCE(mf.player_name, '') <> ''
|
||
THEN 2
|
||
WHEN COALESCE(p.number::text, '') = COALESCE(mf.number, '')
|
||
THEN 3
|
||
ELSE 4
|
||
END,
|
||
p.id
|
||
LIMIT 1
|
||
) p ON TRUE
|
||
WHERE mf.match_id = %s and mf.team_id = %s
|
||
ORDER BY mf.id
|
||
"""
|
||
|
||
conn = get_connection()
|
||
try:
|
||
with conn.cursor() as cur:
|
||
cur.execute(query, (session_token[1], team_id))
|
||
return cur.fetchall()
|
||
finally:
|
||
conn.close()
|
||
|
||
|
||
def get_vmix_scoreboard_info(session_token: str):
|
||
query = """
|
||
WITH yellow_reds AS (
|
||
SELECT
|
||
side,
|
||
player_id
|
||
FROM match_events_ui
|
||
WHERE match_id = %s
|
||
AND type = 'yellow'
|
||
AND player_id IS NOT NULL
|
||
GROUP BY side, player_id
|
||
HAVING COUNT(*) >= 2
|
||
),
|
||
direct_reds AS (
|
||
SELECT
|
||
side,
|
||
COUNT(*) AS cnt
|
||
FROM match_events_ui
|
||
WHERE match_id = %s
|
||
AND type = 'red'
|
||
GROUP BY side
|
||
),
|
||
two_yellow_reds AS (
|
||
SELECT
|
||
side,
|
||
COUNT(*) AS cnt
|
||
FROM yellow_reds
|
||
GROUP BY side
|
||
),
|
||
red_totals AS (
|
||
SELECT
|
||
s.side,
|
||
COALESCE(dr.cnt, 0) + COALESCE(tyr.cnt, 0) AS red_count
|
||
FROM (
|
||
SELECT 'home' AS side
|
||
UNION ALL
|
||
SELECT 'away' AS side
|
||
) s
|
||
LEFT JOIN direct_reds dr ON dr.side = s.side
|
||
LEFT JOIN two_yellow_reds tyr ON tyr.side = s.side
|
||
)
|
||
SELECT
|
||
-- голы
|
||
COALESCE(SUM(
|
||
CASE
|
||
WHEN m.side = 'home' AND m.type IN ('goal', 'penalty', 'own_goal')
|
||
THEN 1 ELSE 0
|
||
END
|
||
), 0) AS home_goals,
|
||
|
||
COALESCE(SUM(
|
||
CASE
|
||
WHEN m.side = 'away' AND m.type IN ('goal', 'penalty', 'own_goal')
|
||
THEN 1 ELSE 0
|
||
END
|
||
), 0) AS away_goals,
|
||
|
||
-- домашние карточки
|
||
CASE WHEN (SELECT red_count FROM red_totals WHERE side = 'home') >= 1 THEN '#FF0000' ELSE '#FF000000' END AS home_red_1,
|
||
CASE WHEN (SELECT red_count FROM red_totals WHERE side = 'home') >= 2 THEN '#FF0000' ELSE '#FF000000' END AS home_red_2,
|
||
CASE WHEN (SELECT red_count FROM red_totals WHERE side = 'home') >= 3 THEN '#FF0000' ELSE '#FF000000' END AS home_red_3,
|
||
CASE WHEN (SELECT red_count FROM red_totals WHERE side = 'home') >= 4 THEN '#FF0000' ELSE '#FF000000' END AS home_red_4,
|
||
|
||
-- гостевые карточки
|
||
CASE WHEN (SELECT red_count FROM red_totals WHERE side = 'away') >= 1 THEN '#FF0000' ELSE '#FF000000' END AS away_red_1,
|
||
CASE WHEN (SELECT red_count FROM red_totals WHERE side = 'away') >= 2 THEN '#FF0000' ELSE '#FF000000' END AS away_red_2,
|
||
CASE WHEN (SELECT red_count FROM red_totals WHERE side = 'away') >= 3 THEN '#FF0000' ELSE '#FF000000' END AS away_red_3,
|
||
CASE WHEN (SELECT red_count FROM red_totals WHERE side = 'away') >= 4 THEN '#FF0000' ELSE '#FF000000' END AS away_red_4
|
||
|
||
FROM match_events_ui m
|
||
WHERE m.match_id = %s;
|
||
"""
|
||
|
||
conn = get_connection()
|
||
try:
|
||
with conn.cursor() as cur:
|
||
cur.execute(query, (session_token[1],session_token[1],session_token[1]))
|
||
return cur.fetchone()
|
||
finally:
|
||
conn.close()
|
||
|
||
|
||
def get_vmix_players_goal2(match_id: int):
|
||
"""запрос для получения игроков, забивших голы, с указанием минут и типа гола (основной, пенальти, автогол)
|
||
Иванов 23', 25' (дубли пишуться вместе)
|
||
"""
|
||
query = """
|
||
WITH goal_events AS (
|
||
SELECT
|
||
meu.side,
|
||
COALESCE(p.last_name, meu.player_name) AS last_name,
|
||
meu.seconds,
|
||
meu.id,
|
||
meu.type,
|
||
CASE
|
||
WHEN EXISTS (
|
||
SELECT 1
|
||
FROM match_events_ui m2
|
||
WHERE m2.match_id = meu.match_id
|
||
AND m2.type = 'period_start_2h'
|
||
AND m2.seconds <= meu.seconds
|
||
) THEN 2
|
||
WHEN EXISTS (
|
||
SELECT 1
|
||
FROM match_events_ui m2
|
||
WHERE m2.match_id = meu.match_id
|
||
AND m2.type = 'period_start_1h'
|
||
AND m2.seconds <= meu.seconds
|
||
) THEN 1
|
||
ELSE NULL
|
||
END AS period_no
|
||
FROM match_events_ui meu
|
||
LEFT JOIN players p ON p.id = meu.player_id
|
||
WHERE meu.match_id = %s
|
||
AND meu.type IN ('goal', 'penalty', 'own_goal')
|
||
),
|
||
grouped_players AS (
|
||
SELECT
|
||
side,
|
||
last_name,
|
||
MIN(seconds) AS first_goal_seconds,
|
||
STRING_AGG(
|
||
(
|
||
CASE
|
||
WHEN period_no = 1 AND seconds > 2700
|
||
THEN '45''' || '+' || ((seconds - 2700) / 60)::int::text
|
||
|
||
WHEN period_no = 2 AND seconds > 5400
|
||
THEN '90''' || '+' || ((seconds - 5400) / 60)::int::text
|
||
|
||
WHEN period_no = 2
|
||
THEN (46 + ((seconds - 2700) / 60)::int)::text || ''''
|
||
|
||
ELSE (1 + (seconds / 60)::int)::text || ''''
|
||
END
|
||
) ||
|
||
CASE
|
||
WHEN type = 'own_goal' THEN ' (АГ)'
|
||
WHEN type = 'penalty' THEN ' (П)'
|
||
ELSE ''
|
||
END,
|
||
', ' ORDER BY seconds, id
|
||
) AS goal_minutes
|
||
FROM goal_events
|
||
GROUP BY side, last_name
|
||
),
|
||
home_rows AS (
|
||
SELECT
|
||
ROW_NUMBER() OVER (ORDER BY first_goal_seconds, last_name) AS rn,
|
||
last_name || ' ' || goal_minutes AS player_name1
|
||
FROM grouped_players
|
||
WHERE side = 'home'
|
||
),
|
||
away_rows AS (
|
||
SELECT
|
||
ROW_NUMBER() OVER (ORDER BY first_goal_seconds, last_name) AS rn,
|
||
last_name || ' ' || goal_minutes AS player_name2
|
||
FROM grouped_players
|
||
WHERE side = 'away'
|
||
)
|
||
SELECT
|
||
COALESCE(h.player_name1, '') AS player_name1,
|
||
COALESCE(a.player_name2, '') AS player_name2
|
||
FROM home_rows h
|
||
FULL OUTER JOIN away_rows a ON a.rn = h.rn
|
||
ORDER BY COALESCE(h.rn, a.rn);
|
||
"""
|
||
|
||
conn = get_connection()
|
||
try:
|
||
with conn.cursor() as cur:
|
||
cur.execute(query, (match_id,))
|
||
return cur.fetchall()
|
||
finally:
|
||
conn.close()
|
||
|
||
def get_vmix_players_goal(match_id: int):
|
||
"""запрос для получения игроков, забивших голы, с указанием минут и типа гола (основной, пенальти, автогол)
|
||
Каждый гол в отдельной строке, дубли по игроку не объединяются
|
||
"""
|
||
query = """
|
||
WITH goal_events AS (
|
||
SELECT
|
||
meu.side,
|
||
COALESCE(p.last_name, meu.player_name) AS last_name,
|
||
meu.seconds,
|
||
meu.id,
|
||
meu.type,
|
||
CASE
|
||
WHEN EXISTS (
|
||
SELECT 1
|
||
FROM match_events_ui m2
|
||
WHERE m2.match_id = meu.match_id
|
||
AND m2.type = 'period_start_2h'
|
||
AND m2.seconds <= meu.seconds
|
||
) THEN 2
|
||
WHEN EXISTS (
|
||
SELECT 1
|
||
FROM match_events_ui m2
|
||
WHERE m2.match_id = meu.match_id
|
||
AND m2.type = 'period_start_1h'
|
||
AND m2.seconds <= meu.seconds
|
||
) THEN 1
|
||
ELSE NULL
|
||
END AS period_no
|
||
FROM match_events_ui meu
|
||
LEFT JOIN players p ON p.id = meu.player_id
|
||
WHERE meu.match_id = %s
|
||
AND meu.type IN ('goal', 'penalty', 'own_goal')
|
||
),
|
||
goal_rows AS (
|
||
SELECT
|
||
side,
|
||
last_name,
|
||
seconds,
|
||
id,
|
||
last_name || ' ' ||
|
||
(
|
||
CASE
|
||
WHEN period_no = 1 AND seconds > 2700
|
||
THEN '45''' || '+' || ((seconds - 2700) / 60)::int::text
|
||
|
||
WHEN period_no = 2 AND seconds > 5400
|
||
THEN '90''' || '+' || ((seconds - 5400) / 60)::int::text
|
||
|
||
WHEN period_no = 2
|
||
THEN (46 + ((seconds - 2700) / 60)::int)::text || ''''
|
||
|
||
ELSE (1 + (seconds / 60)::int)::text || ''''
|
||
END
|
||
) ||
|
||
CASE
|
||
WHEN type = 'own_goal' THEN ' (АГ)'
|
||
WHEN type = 'penalty' THEN ' (П)'
|
||
ELSE ''
|
||
END AS player_goal
|
||
FROM goal_events
|
||
),
|
||
home_rows AS (
|
||
SELECT
|
||
ROW_NUMBER() OVER (ORDER BY seconds, id, last_name) AS rn,
|
||
player_goal AS player_name1
|
||
FROM goal_rows
|
||
WHERE side = 'home'
|
||
),
|
||
away_rows AS (
|
||
SELECT
|
||
ROW_NUMBER() OVER (ORDER BY seconds, id, last_name) AS rn,
|
||
player_goal AS player_name2
|
||
FROM goal_rows
|
||
WHERE side = 'away'
|
||
)
|
||
SELECT
|
||
COALESCE(h.player_name1, '') AS player_name1,
|
||
COALESCE(a.player_name2, '') AS player_name2
|
||
FROM home_rows h
|
||
FULL OUTER JOIN away_rows a ON a.rn = h.rn
|
||
ORDER BY COALESCE(h.rn, a.rn);
|
||
"""
|
||
|
||
conn = get_connection()
|
||
try:
|
||
with conn.cursor() as cur:
|
||
cur.execute(query, (match_id,))
|
||
return cur.fetchall()
|
||
finally:
|
||
conn.close()
|