From 4509850a292058c62b0c99ab241954d5bfde48b4 Mon Sep 17 00:00:00 2001 From: rgcosta Date: Sun, 12 Apr 2026 15:22:32 +0000 Subject: [PATCH] Update api/main.py Clean up --- api/main.py | 129 ++++++++++++++++++++++------------------------------ 1 file changed, 54 insertions(+), 75 deletions(-) diff --git a/api/main.py b/api/main.py index 6f7439a..6eafb6c 100644 --- a/api/main.py +++ b/api/main.py @@ -4,11 +4,12 @@ FC Porto fixtures API Endpoints: GET /health — liveness GET /data — raw JSON - GET /next — next match (for Homepage customapi) - GET /widget — self-contained HTML widget for Homepage iframe + GET /next — next match + GET /widget — HTML widget for Homepage iframe """ import json +import re from datetime import datetime from pathlib import Path from zoneinfo import ZoneInfo @@ -94,87 +95,66 @@ def next_match(): @app.get("/widget", response_class=HTMLResponse) def widget(): - """ - Layout: 3 past results + 2 upcoming fixtures - - For each match card: - [ home badge ] - [ home name ] - [ score/date ] - [ away badge ] - [ away name ] - [ comp abbr ] - """ try: d = load() past, future = sorted_matches(d) except HTTPException: past, future = [], [] - # Last 3 past + next 2 future - last3 = past[-3:] if len(past) >= 3 else past + # Most recent 3 past (newest leftmost = reversed) + next 2 future + last3 = list(reversed(past[-3:])) if len(past) >= 3 else list(reversed(past)) next2 = future[:2] if len(future) >= 2 else future - # Pad with empties - empty = { - "home": "—", "away": "—", - "home_logo": None, "away_logo": None, - "competition": "—", "abbr": "—", - "date": "—", "time": "", "score": None, "is_past": True, - } - while len(last3) < 3: - last3.insert(0, dict(empty)) - while len(next2) < 2: - next2.append(dict(empty, is_past=False)) + empty_past = {"home": "—", "away": "—", "home_logo": None, "away_logo": None, + "abbr": "—", "date": "—", "time": "", "score": None} + empty_future = {**empty_past, "score": None} - all_cards = last3 + next2 + while len(last3) < 3: + last3.append(dict(empty_past)) + while len(next2) < 2: + next2.append(dict(empty_future)) PORTO_LOGO = "https://a.espncdn.com/i/teamlogos/soccer/500/437.png" + def clean_time(t: str) -> str: + """Remove ESPN artifacts like 'v', 'v2nd Leg' etc from time field.""" + t = t.strip() + # If it looks like a time (contains :) keep it, otherwise TBD + if re.search(r'\d+:\d+', t): + return re.search(r'\d+:\d+\s*(?:AM|PM)?', t).group(0).strip() + return "TBD" + + def shorten(name: str) -> str: + if len(name) <= 10: + return name + parts = name.split() + return parts[-1] if parts else name[:10] + def card(m: dict, is_past: bool) -> str: - home = m.get("home", "—") - away = m.get("away", "—") home_logo = m.get("home_logo") or PORTO_LOGO away_logo = m.get("away_logo") or PORTO_LOGO + home_s = shorten(m.get("home", "—")) + away_s = shorten(m.get("away", "—")) abbr = m.get("abbr", "—") score = m.get("score") date = m.get("date", "—") - time_ = m.get("time", "") - time_str = m.get("time", "") - - - time_str = re.sub(r'^v\s*', '', time_str).strip() - time_str = time_str if time_str not in ("", "TBD", "v") else "TBD" - - - # Shorten names for display - def shorten(name: str) -> str: - if len(name) <= 10: - return name - parts = name.split() - return parts[-1] if parts else name[:10] - - home_s = shorten(home) - away_s = shorten(away) + time_raw = m.get("time", "") if is_past and score: - middle = f'
{score}
' - cls = "card past" + middle = f'
{score}
' + cls = "card past" else: - date_s = "/".join(date.split("/")[:2]) if "/" in date else date - time_part = f"
{time_str}" if time_str and time_str != "TBD" else "" + date_s = "/".join(date.split("/")[:2]) if "/" in date else date + time_clean = clean_time(time_raw) + time_part = f"
{time_clean}" if time_clean != "TBD" else "" middle = f'
{date_s}{time_part}
' + cls = "card future" - time_s = f"
{time_}" if time_ and time_ != "TBD" else "" - - cls = "card future" - - return f""" -
- {home} + return f"""
+
{home_s}
{middle} - {away} +
{away_s}
{abbr}
""" @@ -197,6 +177,12 @@ def widget(): align-items: center; overflow: hidden; }} + .container {{ + display: flex; + align-items: center; + width: 100%; + padding: 0 4px; + }} .section {{ display: flex; align-items: stretch; @@ -205,33 +191,26 @@ def widget(): .divider {{ width: 1px; background: #334155; - margin: 4px 6px; + margin: 4px 8px; align-self: stretch; flex-shrink: 0; }} - .container {{ - display: flex; - align-items: center; - width: 100%; - padding: 0 4px; - }} .card {{ flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; - padding: 2px 4px; - gap: 1px; - min-width: 0; + padding: 2px 6px; + gap: 2px; + min-width: 60px; }} - .past {{ opacity: 0.7; }} + .past {{ opacity: 0.7; }} .future {{ opacity: 1; }} .badge {{ - width: 22px; - height: 22px; + width: 24px; + height: 24px; object-fit: contain; - flex-shrink: 0; }} .name {{ font-size: 9px; @@ -245,16 +224,16 @@ def widget(): .home-name {{ color: #cbd5e1; }} .away-name {{ color: #94a3b8; }} .score {{ - font-size: 11px; + font-size: 12px; font-weight: 800; color: #4ade80; - padding: 1px 0; + padding: 2px 0; }} .fixture {{ font-size: 9px; color: #93c5fd; text-align: center; - line-height: 1.3; + line-height: 1.4; }} .comp {{ font-size: 8px;