This commit is contained in:
Tomas Dvorak
2026-01-26 08:13:18 +01:00
parent aa036b6550
commit dfc079288f
505 changed files with 95755 additions and 5712 deletions
+200
View File
@@ -0,0 +1,200 @@
import argparse
import json
from pathlib import Path
from typing import List, Dict
from openpyxl import Workbook
from openpyxl.styles import Font
from openpyxl.utils import get_column_letter
MATCH_FIELDS: List[Dict[str, str]] = [
{
"name": "competition_code",
"label": "Kód soutěže",
"help": "Např. A1A. Použije se, pokud není vyplněno competition_external_id.",
},
{
"name": "competition_external_id",
"label": "External ID soutěže (UUID)",
"help": "UUID soutěže z odkazu na fotbal.cz. Má přednost před competition_code.",
},
{"name": "round", "label": "Kolo", "help": "Např. 2. kolo, 10. kolo atd."},
{
"name": "is_home",
"label": "Domácí / venku",
"help": "Hodnota home/away (nebo 1/0, true/false, yes/no).",
},
{"name": "opponent_name", "label": "Soupeř", "help": "Název soupeře."},
{
"name": "opponent_club_link",
"label": "Odkaz na klub soupeře",
"help": "URL na klub soupeře z fotbal.cz z URL se přečte UUID pro loga.",
},
{
"name": "external_match_id",
"label": "External ID zápasu (UUID)",
"help": "UUID zápasu; pokud není, vezme se UUID z match_link.",
},
{
"name": "kickoff_date",
"label": "Datum výkopu (YYYY-MM-DD)",
"help": "Datum zápasu, např. 2025-03-15.",
},
{
"name": "kickoff_time",
"label": "Čas výkopu (HH:MM)",
"help": "Čas zápasu, např. 17:00.",
},
{
"name": "score_fulltime",
"label": "Konečný stav",
"help": "Např. 2:1. Může být prázdné pro budoucí zápasy.",
},
{
"name": "score_halftime",
"label": "Poločasu",
"help": "Např. 1:0. Volitelné.",
},
{
"name": "match_link",
"label": "Odkaz na zápas",
"help": "URL na detail zápasu na is.fotbal.cz.",
},
{"name": "venue", "label": "Hřiště", "help": "Název hřiště / stadionu."},
{"name": "note", "label": "Poznámka", "help": "Libovolná poznámka k zápasu."},
]
TABLE_FIELDS: List[Dict[str, str]] = [
{
"name": "competition_code",
"label": "Kód soutěže",
"help": "Např. A1A. Použije se, pokud není vyplněno competition_external_id.",
},
{
"name": "competition_external_id",
"label": "External ID soutěže (UUID)",
"help": "UUID soutěže z odkazu na fotbal.cz. Má přednost před competition_code.",
},
{"name": "rank", "label": "Pořadí", "help": "Pozice v tabulce, např. 1."},
{"name": "team_name", "label": "Tým", "help": "Název týmu."},
{
"name": "team_club_link",
"label": "Odkaz na klub týmu",
"help": "URL na klub z fotbal.cz z URL se přečte UUID pro loga.",
},
{"name": "played", "label": "Zápasy", "help": "Počet odehraných zápasů."},
{"name": "wins", "label": "Výhry", "help": "Počet výher."},
{"name": "draws", "label": "Remízy", "help": "Počet remíz."},
{"name": "losses", "label": "Prohry", "help": "Počet proher."},
{
"name": "score",
"label": "Skóre",
"help": "Souhrnné skóre ve formátu góly:inkasované, např. 45:17.",
},
{"name": "points", "label": "Body", "help": "Počet bodů v tabulce."},
]
def _auto_width(sheet):
for column_cells in sheet.columns:
max_length = 0
column = column_cells[0].column
for cell in column_cells:
value = str(cell.value) if cell.value is not None else ""
if len(value) > max_length:
max_length = len(value)
adjusted = max(max_length + 2, 12)
sheet.column_dimensions[get_column_letter(column)].width = adjusted
def generate_workbook(output_dir: Path) -> None:
wb = Workbook()
ws_matches = wb.active
ws_matches.title = "matches"
ws_matches.append([f["name"] for f in MATCH_FIELDS])
for cell in ws_matches[1]:
cell.font = Font(bold=True)
_auto_width(ws_matches)
ws_tables = wb.create_sheet(title="tables")
ws_tables.append([f["name"] for f in TABLE_FIELDS])
for cell in ws_tables[1]:
cell.font = Font(bold=True)
_auto_width(ws_tables)
path = output_dir / "manual_facr_templates.xlsx"
wb.save(path)
def generate_forms_spec(output_dir: Path) -> None:
spec = {
"matches_form": {
"title": "Manuální zápasy (FAČR)",
"description": "Formulář pro ruční zadání zápasů ve struktuře odpovídající CSV/XLSX/JSON importu.",
"fields": [
{
"name": f["name"],
"label": f["label"],
"help_text": f["help"],
"type": "text",
"required": f["name"]
in {"competition_code", "competition_external_id", "is_home", "opponent_name"},
}
for f in MATCH_FIELDS
],
},
"tables_form": {
"title": "Manuální tabulky (FAČR)",
"description": "Formulář pro ruční zadání tabulek soutěží ve struktuře odpovídající CSV/XLSX/JSON importu.",
"fields": [
{
"name": f["name"],
"label": f["label"],
"help_text": f["help"],
"type": "text",
"required": f["name"]
in {"competition_code", "competition_external_id", "rank", "team_name"},
}
for f in TABLE_FIELDS
],
},
}
path = output_dir / "manual_facr_google_forms.json"
path.write_text(json.dumps(spec, ensure_ascii=False, indent=2), encoding="utf-8")
def main() -> None:
parser = argparse.ArgumentParser(
description=(
"Generate manual FACR Excel templates (matches/tables) and a Google Forms "
"field specification from a single schema."
)
)
parser.add_argument(
"--output-dir",
type=str,
default=None,
help=(
"Output directory for generated files. "
"Defaults to <project_root>/static/manual-facr."
),
)
args = parser.parse_args()
if args.output_dir:
output_dir = Path(args.output_dir)
else:
output_dir = Path(__file__).resolve().parents[1] / "static" / "manual-facr"
output_dir.mkdir(parents=True, exist_ok=True)
generate_workbook(output_dir)
generate_forms_spec(output_dir)
if __name__ == "__main__":
main()