логотипы каналов, тест 2
This commit is contained in:
13
app.py
13
app.py
@@ -34,7 +34,7 @@ from parsers.parser_players import run_parser_players
|
|||||||
from parsers.parser_schedule import run_parser_schedule
|
from parsers.parser_schedule import run_parser_schedule
|
||||||
from parsers.parser_standings import run_parser_standings
|
from parsers.parser_standings import run_parser_standings
|
||||||
from parsers.parser_teams import run_parser_teams
|
from parsers.parser_teams import run_parser_teams
|
||||||
from parsers.parser_sources import build_empty_photo_path, list_parser_sources, get_default_source_key, get_parser_source
|
from parsers.parser_sources import build_empty_photo_path, build_channel_logo_path, build_empty_channel_logo_path, list_parser_sources, get_default_source_key, get_parser_source
|
||||||
from services.project_settings_service import build_project_settings_context, save_project_settings
|
from services.project_settings_service import build_project_settings_context, save_project_settings
|
||||||
from repositories.project_settings_repository import ensure_project_settings_tables
|
from repositories.project_settings_repository import ensure_project_settings_tables
|
||||||
|
|
||||||
@@ -1681,11 +1681,12 @@ def vmix_schedule(session_token: str):
|
|||||||
"#FFFFFF00" if row[2] is None and row[3] is None else "#FFFFFF"
|
"#FFFFFF00" if row[2] is None and row[3] is None else "#FFFFFF"
|
||||||
),
|
),
|
||||||
"channel": (
|
"channel": (
|
||||||
(rf"D:\Графика\ФУТБОЛ\Женская Суперлига 2026\Лого каналов\{row[7]}.png" if row[-1] != "RUSSIAN_CUP" else rf"D:\Графика\ФУТБОЛ\ЖФЛ Кубок России 2026\Лого каналов\{row[7]}.png")
|
build_channel_logo_path(row[8] if len(row) > 8 else None, row[7])
|
||||||
# if row[7] and row[5] == "scheduled"
|
if str(row[7] or "").strip()
|
||||||
if row[2] is None and row[3] is None
|
else build_empty_channel_logo_path(row[8] if len(row) > 8 else None)
|
||||||
else (r"D:\Графика\ФУТБОЛ\Женская Суперлига 2026\Лого каналов\EMPTY.png" if row[-1] != "RUSSIAN_CUP" else r"D:\Графика\ФУТБОЛ\ЖФЛ Кубок России 2026\Лого каналов\EMPTY.png")
|
|
||||||
),
|
),
|
||||||
|
"channel_value": row[7] or "",
|
||||||
|
"source_key": row[8] if len(row) > 8 else "",
|
||||||
}
|
}
|
||||||
for row in rows
|
for row in rows
|
||||||
]
|
]
|
||||||
@@ -2299,12 +2300,14 @@ def render_admin_db_index(
|
|||||||
"title": "Суперлига 2026",
|
"title": "Суперлига 2026",
|
||||||
"logo_base_path": r"D:\Графика\ФУТБОЛ\Женская Суперлига 2026\Teams Logos",
|
"logo_base_path": r"D:\Графика\ФУТБОЛ\Женская Суперлига 2026\Teams Logos",
|
||||||
"photo_base_path": r"D:\Графика\ФУТБОЛ\Женская Суперлига 2026\Photo",
|
"photo_base_path": r"D:\Графика\ФУТБОЛ\Женская Суперлига 2026\Photo",
|
||||||
|
"channel_logo_base_path": r"D:\Графика\ФУТБОЛ\Женская Суперлига 2026\Лого каналов",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "RUSSIAN_CUP",
|
"key": "RUSSIAN_CUP",
|
||||||
"title": "Кубок России 2026",
|
"title": "Кубок России 2026",
|
||||||
"logo_base_path": r"D:\Графика\ФУТБОЛ\Кубок России 2026\Teams Logos",
|
"logo_base_path": r"D:\Графика\ФУТБОЛ\Кубок России 2026\Teams Logos",
|
||||||
"photo_base_path": r"D:\Графика\ФУТБОЛ\Кубок России 2026\Photo",
|
"photo_base_path": r"D:\Графика\ФУТБОЛ\Кубок России 2026\Photo",
|
||||||
|
"channel_logo_base_path": r"D:\Графика\ФУТБОЛ\ЖФЛ Кубок России 2026\Лого каналов",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
default_parser_source_key = "SUPERLEAGUE"
|
default_parser_source_key = "SUPERLEAGUE"
|
||||||
|
|||||||
@@ -228,3 +228,45 @@ def build_photo_path(source_key: str | None, filename: str | None) -> str:
|
|||||||
def build_empty_photo_path(source_key: str | None = None) -> str:
|
def build_empty_photo_path(source_key: str | None = None) -> str:
|
||||||
"""Путь к пустой картинке фото для выбранного источника."""
|
"""Путь к пустой картинке фото для выбранного источника."""
|
||||||
return build_photo_path(source_key, "EMPTY.png")
|
return build_photo_path(source_key, "EMPTY.png")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def extract_channel_logo_filename(value: str | None) -> str:
|
||||||
|
"""Возвращает имя файла логотипа телеканала из старого полного пути или нового значения."""
|
||||||
|
value = str(value or "").strip().strip('"').strip("'")
|
||||||
|
if not value:
|
||||||
|
return ""
|
||||||
|
normalized = value.replace("/", "\\")
|
||||||
|
filename = normalized.split("\\")[-1].strip()
|
||||||
|
return filename
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_channel_logo_extension(filename: str) -> str:
|
||||||
|
filename = extract_channel_logo_filename(filename)
|
||||||
|
if not filename:
|
||||||
|
return ""
|
||||||
|
if "." not in filename.rsplit("\\", 1)[-1]:
|
||||||
|
return f"{filename}.png"
|
||||||
|
return filename
|
||||||
|
|
||||||
|
|
||||||
|
def get_channel_logo_base_path(source_key: str | None = None) -> str:
|
||||||
|
source = get_parser_source(source_key)
|
||||||
|
return str(source.get("channel_logo_base_path") or "").rstrip("\\/")
|
||||||
|
|
||||||
|
|
||||||
|
def build_channel_logo_path(source_key: str | None, filename: str | None) -> str:
|
||||||
|
"""Собирает полный путь к логотипу телеканала по источнику турнира."""
|
||||||
|
logo_file = ensure_channel_logo_extension(filename)
|
||||||
|
if not logo_file:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
base_path = get_channel_logo_base_path(source_key)
|
||||||
|
if not base_path:
|
||||||
|
return logo_file
|
||||||
|
|
||||||
|
return f"{base_path}\\{logo_file}"
|
||||||
|
|
||||||
|
|
||||||
|
def build_empty_channel_logo_path(source_key: str | None = None) -> str:
|
||||||
|
return build_channel_logo_path(source_key, "EMPTY.png")
|
||||||
|
|||||||
@@ -355,7 +355,7 @@ def get_tour_schedule_by_match_id(match_id: int) -> list[dict]:
|
|||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
"""
|
||||||
SELECT m.tour
|
SELECT m.tour, m.season, m.source_key
|
||||||
FROM matches m
|
FROM matches m
|
||||||
WHERE m.id = %s
|
WHERE m.id = %s
|
||||||
""",
|
""",
|
||||||
@@ -366,6 +366,8 @@ def get_tour_schedule_by_match_id(match_id: int) -> list[dict]:
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
tour = row[0]
|
tour = row[0]
|
||||||
|
season = row[1]
|
||||||
|
source_key = resolve_match_source_key(match_id, row[2] if len(row) > 2 else None)
|
||||||
|
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
"""
|
||||||
@@ -384,11 +386,13 @@ def get_tour_schedule_by_match_id(match_id: int) -> list[dict]:
|
|||||||
LEFT JOIN teams ht ON ht.id = m.home_team_id
|
LEFT JOIN teams ht ON ht.id = m.home_team_id
|
||||||
LEFT JOIN teams at ON at.id = m.away_team_id
|
LEFT JOIN teams at ON at.id = m.away_team_id
|
||||||
WHERE m.tour = %s
|
WHERE m.tour = %s
|
||||||
|
AND (%s IS NULL OR m.season = %s)
|
||||||
|
AND COALESCE(NULLIF(m.source_key, ''), %s) = %s
|
||||||
ORDER BY
|
ORDER BY
|
||||||
m.match_date NULLS LAST,
|
m.match_date NULLS LAST,
|
||||||
m.id
|
m.id
|
||||||
""",
|
""",
|
||||||
(tour,),
|
(tour, season, season, source_key, source_key),
|
||||||
)
|
)
|
||||||
rows = cur.fetchall()
|
rows = cur.fetchall()
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ DEFAULT_PARSER_SOURCES = [
|
|||||||
"calendar_type": "tours",
|
"calendar_type": "tours",
|
||||||
"logo_base_path": r"D:\Графика\ФУТБОЛ\Женская Суперлига 2026\Teams Logos",
|
"logo_base_path": r"D:\Графика\ФУТБОЛ\Женская Суперлига 2026\Teams Logos",
|
||||||
"photo_base_path": r"D:\Графика\ФУТБОЛ\Женская Суперлига 2026\Photo",
|
"photo_base_path": r"D:\Графика\ФУТБОЛ\Женская Суперлига 2026\Photo",
|
||||||
|
"channel_logo_base_path": r"D:\Графика\ФУТБОЛ\Женская Суперлига 2026\Лого каналов",
|
||||||
"teams_url": "https://wfl.rfs.ru/tournament/1061879/teams",
|
"teams_url": "https://wfl.rfs.ru/tournament/1061879/teams",
|
||||||
"schedule_url": "https://wfl.rfs.ru/tournament/1061879/calendar?round_id=1117550&type=tours",
|
"schedule_url": "https://wfl.rfs.ru/tournament/1061879/calendar?round_id=1117550&type=tours",
|
||||||
"standings_url": "https://wfl.rfs.ru/tournament/1061879/tables",
|
"standings_url": "https://wfl.rfs.ru/tournament/1061879/tables",
|
||||||
@@ -38,6 +39,7 @@ DEFAULT_PARSER_SOURCES = [
|
|||||||
"calendar_type": "stages",
|
"calendar_type": "stages",
|
||||||
"logo_base_path": r"D:\Графика\ФУТБОЛ\Кубок России 2026\Teams Logos",
|
"logo_base_path": r"D:\Графика\ФУТБОЛ\Кубок России 2026\Teams Logos",
|
||||||
"photo_base_path": r"D:\Графика\ФУТБОЛ\Кубок России 2026\Photo",
|
"photo_base_path": r"D:\Графика\ФУТБОЛ\Кубок России 2026\Photo",
|
||||||
|
"channel_logo_base_path": r"D:\Графика\ФУТБОЛ\ЖФЛ Кубок России 2026\Лого каналов",
|
||||||
"teams_url": "https://wfl.rfs.ru/tournament/1064908/teams",
|
"teams_url": "https://wfl.rfs.ru/tournament/1064908/teams",
|
||||||
"schedule_url": "https://wfl.rfs.ru/tournament/1064908/calendar?round_id=1125159&type=stages",
|
"schedule_url": "https://wfl.rfs.ru/tournament/1064908/calendar?round_id=1125159&type=stages",
|
||||||
"standings_url": "https://wfl.rfs.ru/tournament/1064908/tables",
|
"standings_url": "https://wfl.rfs.ru/tournament/1064908/tables",
|
||||||
@@ -49,6 +51,15 @@ DEFAULT_PARSER_SOURCES = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
SOURCE_SELECT_SQL = """
|
||||||
|
SELECT key, title, tournament_id, round_id, season, calendar_type,
|
||||||
|
logo_base_path, photo_base_path, channel_logo_base_path,
|
||||||
|
teams_url, schedule_url, standings_url,
|
||||||
|
match_base_url, base_url, sort_order, is_active
|
||||||
|
FROM parser_sources
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
def _row_to_source(row: tuple) -> dict[str, Any]:
|
def _row_to_source(row: tuple) -> dict[str, Any]:
|
||||||
return {
|
return {
|
||||||
"key": row[0] or "",
|
"key": row[0] or "",
|
||||||
@@ -59,13 +70,14 @@ def _row_to_source(row: tuple) -> dict[str, Any]:
|
|||||||
"calendar_type": row[5] or "tours",
|
"calendar_type": row[5] or "tours",
|
||||||
"logo_base_path": row[6] or "",
|
"logo_base_path": row[6] or "",
|
||||||
"photo_base_path": row[7] or "",
|
"photo_base_path": row[7] or "",
|
||||||
"teams_url": row[8] or "",
|
"channel_logo_base_path": row[8] or "",
|
||||||
"schedule_url": row[9] or "",
|
"teams_url": row[9] or "",
|
||||||
"standings_url": row[10] or "",
|
"schedule_url": row[10] or "",
|
||||||
"match_base_url": row[11] or "",
|
"standings_url": row[11] or "",
|
||||||
"base_url": row[12] or "https://wfl.rfs.ru",
|
"match_base_url": row[12] or "",
|
||||||
"sort_order": row[13] or 0,
|
"base_url": row[13] or "https://wfl.rfs.ru",
|
||||||
"is_active": bool(row[14]),
|
"sort_order": row[14] or 0,
|
||||||
|
"is_active": bool(row[15]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -94,6 +106,7 @@ def ensure_project_settings_tables() -> None:
|
|||||||
calendar_type VARCHAR(50) NOT NULL DEFAULT 'tours',
|
calendar_type VARCHAR(50) NOT NULL DEFAULT 'tours',
|
||||||
logo_base_path TEXT,
|
logo_base_path TEXT,
|
||||||
photo_base_path TEXT,
|
photo_base_path TEXT,
|
||||||
|
channel_logo_base_path TEXT,
|
||||||
teams_url TEXT,
|
teams_url TEXT,
|
||||||
schedule_url TEXT,
|
schedule_url TEXT,
|
||||||
standings_url TEXT,
|
standings_url TEXT,
|
||||||
@@ -107,6 +120,7 @@ def ensure_project_settings_tables() -> None:
|
|||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
cur.execute("ALTER TABLE parser_sources ADD COLUMN IF NOT EXISTS photo_base_path TEXT;")
|
cur.execute("ALTER TABLE parser_sources ADD COLUMN IF NOT EXISTS photo_base_path TEXT;")
|
||||||
|
cur.execute("ALTER TABLE parser_sources ADD COLUMN IF NOT EXISTS channel_logo_base_path TEXT;")
|
||||||
|
|
||||||
for key, value in DEFAULT_APP_SETTINGS.items():
|
for key, value in DEFAULT_APP_SETTINGS.items():
|
||||||
cur.execute(
|
cur.execute(
|
||||||
@@ -122,25 +136,12 @@ def ensure_project_settings_tables() -> None:
|
|||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO parser_sources (
|
INSERT INTO parser_sources (
|
||||||
key,
|
key, title, tournament_id, round_id, season, calendar_type,
|
||||||
title,
|
logo_base_path, photo_base_path, channel_logo_base_path,
|
||||||
tournament_id,
|
teams_url, schedule_url, standings_url, match_base_url, base_url,
|
||||||
round_id,
|
sort_order, is_active, created_at, updated_at
|
||||||
season,
|
|
||||||
calendar_type,
|
|
||||||
logo_base_path,
|
|
||||||
photo_base_path,
|
|
||||||
teams_url,
|
|
||||||
schedule_url,
|
|
||||||
standings_url,
|
|
||||||
match_base_url,
|
|
||||||
base_url,
|
|
||||||
sort_order,
|
|
||||||
is_active,
|
|
||||||
created_at,
|
|
||||||
updated_at
|
|
||||||
)
|
)
|
||||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW())
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW())
|
||||||
ON CONFLICT (key) DO NOTHING;
|
ON CONFLICT (key) DO NOTHING;
|
||||||
""",
|
""",
|
||||||
(
|
(
|
||||||
@@ -152,6 +153,7 @@ def ensure_project_settings_tables() -> None:
|
|||||||
source["calendar_type"],
|
source["calendar_type"],
|
||||||
source["logo_base_path"],
|
source["logo_base_path"],
|
||||||
source["photo_base_path"],
|
source["photo_base_path"],
|
||||||
|
source["channel_logo_base_path"],
|
||||||
source["teams_url"],
|
source["teams_url"],
|
||||||
source["schedule_url"],
|
source["schedule_url"],
|
||||||
source["standings_url"],
|
source["standings_url"],
|
||||||
@@ -172,6 +174,15 @@ def ensure_project_settings_tables() -> None:
|
|||||||
""",
|
""",
|
||||||
(source.get("photo_base_path") or "", source["key"]),
|
(source.get("photo_base_path") or "", source["key"]),
|
||||||
)
|
)
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
UPDATE parser_sources
|
||||||
|
SET channel_logo_base_path = %s, updated_at = NOW()
|
||||||
|
WHERE key = %s
|
||||||
|
AND (channel_logo_base_path IS NULL OR TRIM(channel_logo_base_path) = '');
|
||||||
|
""",
|
||||||
|
(source.get("channel_logo_base_path") or "", source["key"]),
|
||||||
|
)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
except Exception:
|
except Exception:
|
||||||
conn.rollback()
|
conn.rollback()
|
||||||
@@ -223,26 +234,9 @@ def list_parser_sources_from_db(active_only: bool = True) -> list[dict[str, Any]
|
|||||||
try:
|
try:
|
||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
if active_only:
|
if active_only:
|
||||||
cur.execute(
|
cur.execute(SOURCE_SELECT_SQL + " WHERE is_active = TRUE ORDER BY sort_order ASC, title ASC;")
|
||||||
"""
|
|
||||||
SELECT key, title, tournament_id, round_id, season, calendar_type,
|
|
||||||
logo_base_path, photo_base_path, teams_url, schedule_url, standings_url,
|
|
||||||
match_base_url, base_url, sort_order, is_active
|
|
||||||
FROM parser_sources
|
|
||||||
WHERE is_active = TRUE
|
|
||||||
ORDER BY sort_order ASC, title ASC;
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
cur.execute(
|
cur.execute(SOURCE_SELECT_SQL + " ORDER BY sort_order ASC, title ASC;")
|
||||||
"""
|
|
||||||
SELECT key, title, tournament_id, round_id, season, calendar_type,
|
|
||||||
logo_base_path, photo_base_path, teams_url, schedule_url, standings_url,
|
|
||||||
match_base_url, base_url, sort_order, is_active
|
|
||||||
FROM parser_sources
|
|
||||||
ORDER BY sort_order ASC, title ASC;
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
rows = cur.fetchall()
|
rows = cur.fetchall()
|
||||||
return [_row_to_source(row) for row in rows]
|
return [_row_to_source(row) for row in rows]
|
||||||
finally:
|
finally:
|
||||||
@@ -257,17 +251,7 @@ def get_parser_source_from_db(source_key: str) -> dict[str, Any] | None:
|
|||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
try:
|
try:
|
||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
cur.execute(
|
cur.execute(SOURCE_SELECT_SQL + " WHERE key = %s LIMIT 1;", (source_key,))
|
||||||
"""
|
|
||||||
SELECT key, title, tournament_id, round_id, season, calendar_type,
|
|
||||||
logo_base_path, photo_base_path, teams_url, schedule_url, standings_url,
|
|
||||||
match_base_url, base_url, sort_order, is_active
|
|
||||||
FROM parser_sources
|
|
||||||
WHERE key = %s
|
|
||||||
LIMIT 1;
|
|
||||||
""",
|
|
||||||
(source_key,),
|
|
||||||
)
|
|
||||||
row = cur.fetchone()
|
row = cur.fetchone()
|
||||||
return _row_to_source(row) if row else None
|
return _row_to_source(row) if row else None
|
||||||
finally:
|
finally:
|
||||||
@@ -292,6 +276,7 @@ def update_parser_source(source_key: str, values: dict[str, str]) -> None:
|
|||||||
"calendar_type": "tours",
|
"calendar_type": "tours",
|
||||||
"logo_base_path": "",
|
"logo_base_path": "",
|
||||||
"photo_base_path": "",
|
"photo_base_path": "",
|
||||||
|
"channel_logo_base_path": "",
|
||||||
"teams_url": "",
|
"teams_url": "",
|
||||||
"schedule_url": "",
|
"schedule_url": "",
|
||||||
"standings_url": "",
|
"standings_url": "",
|
||||||
@@ -313,10 +298,11 @@ def update_parser_source(source_key: str, values: dict[str, str]) -> None:
|
|||||||
"""
|
"""
|
||||||
INSERT INTO parser_sources (
|
INSERT INTO parser_sources (
|
||||||
key, title, tournament_id, round_id, season, calendar_type,
|
key, title, tournament_id, round_id, season, calendar_type,
|
||||||
logo_base_path, photo_base_path, teams_url, schedule_url, standings_url,
|
logo_base_path, photo_base_path, channel_logo_base_path,
|
||||||
|
teams_url, schedule_url, standings_url,
|
||||||
match_base_url, base_url, sort_order, is_active, created_at, updated_at
|
match_base_url, base_url, sort_order, is_active, created_at, updated_at
|
||||||
)
|
)
|
||||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW())
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW())
|
||||||
ON CONFLICT (key)
|
ON CONFLICT (key)
|
||||||
DO UPDATE SET
|
DO UPDATE SET
|
||||||
title = EXCLUDED.title,
|
title = EXCLUDED.title,
|
||||||
@@ -326,6 +312,7 @@ def update_parser_source(source_key: str, values: dict[str, str]) -> None:
|
|||||||
calendar_type = EXCLUDED.calendar_type,
|
calendar_type = EXCLUDED.calendar_type,
|
||||||
logo_base_path = EXCLUDED.logo_base_path,
|
logo_base_path = EXCLUDED.logo_base_path,
|
||||||
photo_base_path = EXCLUDED.photo_base_path,
|
photo_base_path = EXCLUDED.photo_base_path,
|
||||||
|
channel_logo_base_path = EXCLUDED.channel_logo_base_path,
|
||||||
teams_url = EXCLUDED.teams_url,
|
teams_url = EXCLUDED.teams_url,
|
||||||
schedule_url = EXCLUDED.schedule_url,
|
schedule_url = EXCLUDED.schedule_url,
|
||||||
standings_url = EXCLUDED.standings_url,
|
standings_url = EXCLUDED.standings_url,
|
||||||
@@ -344,6 +331,7 @@ def update_parser_source(source_key: str, values: dict[str, str]) -> None:
|
|||||||
merged.get("calendar_type") or "tours",
|
merged.get("calendar_type") or "tours",
|
||||||
merged.get("logo_base_path") or "",
|
merged.get("logo_base_path") or "",
|
||||||
merged.get("photo_base_path") or "",
|
merged.get("photo_base_path") or "",
|
||||||
|
merged.get("channel_logo_base_path") or "",
|
||||||
merged.get("teams_url") or "",
|
merged.get("teams_url") or "",
|
||||||
merged.get("schedule_url") or "",
|
merged.get("schedule_url") or "",
|
||||||
merged.get("standings_url") or "",
|
merged.get("standings_url") or "",
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ def build_project_settings_context() -> dict[str, Any]:
|
|||||||
),
|
),
|
||||||
_field(_source_field_key(source_key, "LOGO_BASE_PATH"), "Папка логотипов", source.get("logo_base_path", ""), wide=True),
|
_field(_source_field_key(source_key, "LOGO_BASE_PATH"), "Папка логотипов", source.get("logo_base_path", ""), wide=True),
|
||||||
_field(_source_field_key(source_key, "PHOTO_BASE_PATH"), "Папка фотографий", source.get("photo_base_path", ""), wide=True),
|
_field(_source_field_key(source_key, "PHOTO_BASE_PATH"), "Папка фотографий", source.get("photo_base_path", ""), wide=True),
|
||||||
|
_field(_source_field_key(source_key, "CHANNEL_LOGO_BASE_PATH"), "Папка логотипов каналов", source.get("channel_logo_base_path", ""), wide=True),
|
||||||
_field(_source_field_key(source_key, "TEAMS_URL"), "Ссылка на команды", source.get("teams_url", ""), wide=True),
|
_field(_source_field_key(source_key, "TEAMS_URL"), "Ссылка на команды", source.get("teams_url", ""), wide=True),
|
||||||
_field(_source_field_key(source_key, "SCHEDULE_URL"), "Ссылка на расписание", source.get("schedule_url", ""), wide=True),
|
_field(_source_field_key(source_key, "SCHEDULE_URL"), "Ссылка на расписание", source.get("schedule_url", ""), wide=True),
|
||||||
_field(_source_field_key(source_key, "STANDINGS_URL"), "Ссылка на турнирку", source.get("standings_url", ""), wide=True),
|
_field(_source_field_key(source_key, "STANDINGS_URL"), "Ссылка на турнирку", source.get("standings_url", ""), wide=True),
|
||||||
@@ -123,6 +124,7 @@ def save_project_settings(form_values: dict[str, str]) -> list[str]:
|
|||||||
"CALENDAR_TYPE": "calendar_type",
|
"CALENDAR_TYPE": "calendar_type",
|
||||||
"LOGO_BASE_PATH": "logo_base_path",
|
"LOGO_BASE_PATH": "logo_base_path",
|
||||||
"PHOTO_BASE_PATH": "photo_base_path",
|
"PHOTO_BASE_PATH": "photo_base_path",
|
||||||
|
"CHANNEL_LOGO_BASE_PATH": "channel_logo_base_path",
|
||||||
"TEAMS_URL": "teams_url",
|
"TEAMS_URL": "teams_url",
|
||||||
"SCHEDULE_URL": "schedule_url",
|
"SCHEDULE_URL": "schedule_url",
|
||||||
"STANDINGS_URL": "standings_url",
|
"STANDINGS_URL": "standings_url",
|
||||||
|
|||||||
@@ -460,27 +460,29 @@ def get_vmix_schedule(session_row):
|
|||||||
m.match_date,
|
m.match_date,
|
||||||
m.status,
|
m.status,
|
||||||
m.id,
|
m.id,
|
||||||
m.channel
|
COALESCE(m.channel, '') AS channel,
|
||||||
|
COALESCE(NULLIF(m.source_key, ''), %s) AS source_key
|
||||||
FROM matches m
|
FROM matches m
|
||||||
LEFT JOIN teams t1 ON m.home_team_id = t1.id
|
LEFT JOIN teams t1 ON m.home_team_id = t1.id
|
||||||
LEFT JOIN teams t2 ON m.away_team_id = t2.id
|
LEFT JOIN teams t2 ON m.away_team_id = t2.id
|
||||||
WHERE m.tour = %s
|
WHERE m.tour = %s
|
||||||
AND (%s IS NULL OR m.season = %s)
|
AND (%s IS NULL OR m.season = %s)
|
||||||
AND COALESCE(m.source_key, %s) = %s
|
AND COALESCE(NULLIF(m.source_key, ''), %s) = %s
|
||||||
ORDER BY m.match_date, m.id
|
ORDER BY m.match_date, m.id
|
||||||
"""
|
"""
|
||||||
|
|
||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
try:
|
try:
|
||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
cur.execute(query, (tour, season, season, source_filter, source_filter))
|
cur.execute(query, (source_filter, tour, season, season, source_filter, source_filter))
|
||||||
rows = cur.fetchall()
|
rows = cur.fetchall()
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
for row in rows:
|
for row in rows:
|
||||||
row = list(row)
|
row = list(row)
|
||||||
row[0] = build_logo_variant_path(source_key, row[0], "white")
|
row_source_key = row[8] if len(row) > 8 else source_key
|
||||||
row[1] = build_logo_variant_path(source_key, row[1], "white")
|
row[0] = build_logo_variant_path(row_source_key, row[0], "white")
|
||||||
|
row[1] = build_logo_variant_path(row_source_key, row[1], "white")
|
||||||
result.append(tuple(row))
|
result.append(tuple(row))
|
||||||
return result
|
return result
|
||||||
finally:
|
finally:
|
||||||
|
|||||||
12
sql/006_parser_sources_channel_logo_base_path.sql
Normal file
12
sql/006_parser_sources_channel_logo_base_path.sql
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
ALTER TABLE parser_sources
|
||||||
|
ADD COLUMN IF NOT EXISTS channel_logo_base_path TEXT;
|
||||||
|
|
||||||
|
UPDATE parser_sources
|
||||||
|
SET channel_logo_base_path = 'D:\Графика\ФУТБОЛ\Женская Суперлига 2026\Лого каналов'
|
||||||
|
WHERE key = 'SUPERLEAGUE'
|
||||||
|
AND (channel_logo_base_path IS NULL OR TRIM(channel_logo_base_path) = '');
|
||||||
|
|
||||||
|
UPDATE parser_sources
|
||||||
|
SET channel_logo_base_path = 'D:\Графика\ФУТБОЛ\ЖФЛ Кубок России 2026\Лого каналов'
|
||||||
|
WHERE key = 'RUSSIAN_CUP'
|
||||||
|
AND (channel_logo_base_path IS NULL OR TRIM(channel_logo_base_path) = '');
|
||||||
@@ -542,18 +542,19 @@
|
|||||||
<select class="field-select" id="parserSource" name="parser_source" required>
|
<select class="field-select" id="parserSource" name="parser_source" required>
|
||||||
{% if sources %}
|
{% if sources %}
|
||||||
{% for source in sources %}
|
{% for source in sources %}
|
||||||
<option value="{{ source.key }}" data-logo-base-path="{{ source.logo_base_path or '' }}" data-photo-base-path="{{ source.photo_base_path or '' }}" {% if source.key == default_parser_source_key %}selected{% endif %}>
|
<option value="{{ source.key }}" data-logo-base-path="{{ source.logo_base_path or '' }}" data-photo-base-path="{{ source.photo_base_path or '' }}" data-channel-logo-base-path="{{ source.channel_logo_base_path or '' }}" {% if source.key == default_parser_source_key %}selected{% endif %}>
|
||||||
{{ source.title }}
|
{{ source.title }}
|
||||||
</option>
|
</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<option value="SUPERLEAGUE" data-logo-base-path="D:\Графика\ФУТБОЛ\Женская Суперлига 2026\Teams Logos" data-photo-base-path="D:\Графика\ФУТБОЛ\Женская Суперлига 2026\Photo" selected>Суперлига 2026</option>
|
<option value="SUPERLEAGUE" data-logo-base-path="D:\Графика\ФУТБОЛ\Женская Суперлига 2026\Teams Logos" data-photo-base-path="D:\Графика\ФУТБОЛ\Женская Суперлига 2026\Photo" data-channel-logo-base-path="D:\Графика\ФУТБОЛ\Женская Суперлига 2026\Лого каналов" selected>Суперлига 2026</option>
|
||||||
<option value="RUSSIAN_CUP" data-logo-base-path="D:\Графика\ФУТБОЛ\Кубок России 2026\Teams Logos" data-photo-base-path="D:\Графика\ФУТБОЛ\Кубок России 2026\Photo">Кубок России 2026</option>
|
<option value="RUSSIAN_CUP" data-logo-base-path="D:\Графика\ФУТБОЛ\Кубок России 2026\Teams Logos" data-photo-base-path="D:\Графика\ФУТБОЛ\Кубок России 2026\Photo" data-channel-logo-base-path="D:\Графика\ФУТБОЛ\ЖФЛ Кубок России 2026\Лого каналов">Кубок России 2026</option>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</select>
|
</select>
|
||||||
<div class="helper-text">Список источников хранится в <b>базе данных</b> и редактируется в настройках проекта.</div>
|
<div class="helper-text">Список источников хранится в <b>базе данных</b> и редактируется в настройках проекта.</div>
|
||||||
<div class="helper-text">Путь к логотипам: <b id="selectedLogoBasePath">—</b></div>
|
<div class="helper-text">Путь к логотипам: <b id="selectedLogoBasePath">—</b></div>
|
||||||
<div class="helper-text">Путь к фотографиям: <b id="selectedPhotoBasePath">—</b></div>
|
<div class="helper-text">Путь к фотографиям: <b id="selectedPhotoBasePath">—</b></div>
|
||||||
|
<div class="helper-text">Путь к логотипам каналов: <b id="selectedChannelLogoBasePath">—</b></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@@ -851,6 +852,7 @@
|
|||||||
const parserSourceSelect = document.getElementById("parserSource");
|
const parserSourceSelect = document.getElementById("parserSource");
|
||||||
const selectedLogoBasePath = document.getElementById("selectedLogoBasePath");
|
const selectedLogoBasePath = document.getElementById("selectedLogoBasePath");
|
||||||
const selectedPhotoBasePath = document.getElementById("selectedPhotoBasePath");
|
const selectedPhotoBasePath = document.getElementById("selectedPhotoBasePath");
|
||||||
|
const selectedChannelLogoBasePath = document.getElementById("selectedChannelLogoBasePath");
|
||||||
const openCreateAccountBtn = document.getElementById("openCreateAccountBtn");
|
const openCreateAccountBtn = document.getElementById("openCreateAccountBtn");
|
||||||
const closeCreateAccountBtn = document.getElementById("closeCreateAccountBtn");
|
const closeCreateAccountBtn = document.getElementById("closeCreateAccountBtn");
|
||||||
const accountModal = document.getElementById("accountModal");
|
const accountModal = document.getElementById("accountModal");
|
||||||
@@ -1000,6 +1002,9 @@
|
|||||||
if (selectedPhotoBasePath) {
|
if (selectedPhotoBasePath) {
|
||||||
selectedPhotoBasePath.textContent = option?.dataset?.photoBasePath || "—";
|
selectedPhotoBasePath.textContent = option?.dataset?.photoBasePath || "—";
|
||||||
}
|
}
|
||||||
|
if (selectedChannelLogoBasePath) {
|
||||||
|
selectedChannelLogoBasePath.textContent = option?.dataset?.channelLogoBasePath || "—";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("click", (event) => {
|
document.addEventListener("click", (event) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user