From 8238bdf6dea8d7dccdafacaf05df004763d23712 Mon Sep 17 00:00:00 2001 From: Stijn De Clercq Date: Mon, 25 Jan 2021 00:16:38 +0100 Subject: [PATCH] Clean up Football matches a lot --- cogs/football.py | 34 ++--------- functions/football.py | 134 ++++++++++++++++++++++++++++++++++++++++++ functions/scraping.py | 13 ++++ 3 files changed, 152 insertions(+), 29 deletions(-) create mode 100644 functions/football.py diff --git a/cogs/football.py b/cogs/football.py index b025543..fbe4b98 100644 --- a/cogs/football.py +++ b/cogs/football.py @@ -1,9 +1,9 @@ from bs4 import BeautifulSoup -import datetime from decorators import help from discord.ext import commands from enums.help_categories import Category from functions import checks, config +from functions.football import getMatches import requests import tabulate @@ -25,37 +25,13 @@ class Football(commands.Cog): @jpl.command(name="Matches", aliases=["M"], usage="[Week]*") async def matches(self, ctx, *args): args = list(args) + + # Default is current day if not args: args = [str(config.get("jpl_day"))] + if all(letter.isdigit() for letter in args[0]): - current_day = requests.get("https://api.sporza.be/web/soccer/matchdays/161733/{}".format(args[0])).json() - current_day = current_day["groupedMatches"][0]["matches"] - - # Create dictionaries for every match - matches_formatted = {} - for i, match in enumerate(current_day): - matchDic = {"home": match["homeTeam"]["name"], "away": match["awayTeam"]["name"]} - - # Add date - matchDate = datetime.datetime.strptime(match["startDateTime"].split("+")[0], "%Y-%m-%dT%H:%M:%S.%f") - matchDic["date"] = matchDate.strftime("%d/%m") - matchDic["day"] = self.get_weekday(matchDate.weekday()) - - # TODO check back when there's active games (to find the key in the dict) & add the current time if not over - # Add scores - if match["status"] == "END": # Status != [not_yet_started] whatever it is - matchDic["score"] = "{} - {}".format(match["homeScore"], match["awayScore"]) - else: - # If there's no score, show when the match starts - matchDic["score"] = "{}:{}".format( - ("" if len(str(matchDate.hour)) == 2 else "0") + str(matchDate.hour), # Leading Zero - ("" if len(str(matchDate.minute)) == 2 else "0") + str(matchDate.minute)) # Leading Zero - - matches_formatted[i] = matchDic - - # Put every formatted version of the matches in a list - matchList = list([self.format_match(matches_formatted[match]) for match in matches_formatted]) - await ctx.send("```Jupiler Pro League - Speeldag {}\n\n{}```".format(args[0], tabulate.tabulate(matchList, headers=["Dag", "Datum", "Thuis", "Stand", "Uit", "Tijd"]))) + await ctx.send(getMatches(int(args[0]))) else: return await ctx.send("Dit is geen geldige speeldag.") diff --git a/functions/football.py b/functions/football.py new file mode 100644 index 0000000..47b2efe --- /dev/null +++ b/functions/football.py @@ -0,0 +1,134 @@ +from enum import Enum +from attr import dataclass, field +from functions.timeFormatters import fromString +from functions.scraping import getJPLMatches +from functions.stringFormatters import leadingZero +from datetime import datetime +import tabulate + + +class Status(Enum): + AfterToday = "--:--" + NotStarted = "--:--" + Over = "Einde" + HalfTime = "Rust" + + +@dataclass +class Match: + """ + Class representing a football match between two teams + """ + matchDict: dict + home: str = field(init=False) + homeScore: int = 0 + away: str = field(init=False) + awayScore: int = 0 + start: datetime = field(init=False) + date: str = field(init=False) + weekDay: str = field(init=False) + status: Status = field(init=False) + + def __attrs_post_init__(self): + """ + Parse class attributes out of a dictionary returned from an API request + """ + # The API isn't public, so every single game state is differently formatted + self.status = self._getStatus(self.matchDict[Navigation.Status.value]) + self.home = self.matchDict[Navigation.HomeTeam.value][Navigation.Name.value] + self.away = self.matchDict[Navigation.AwayTeam.value][Navigation.Name.value] + + if self._hasStarted(): + self.homeScore = self.matchDict[Navigation.HomeScore.value] + self.awayScore = self.matchDict[Navigation.AwayScore.value] + + self.start = fromString(self.matchDict["startDateTime"], formatString="%Y-%m-%dT%H:%M:%S.%f%z") + self.date = self.start.strftime("%d/%m") + self.weekDay = self._getWeekday() + + def _getStatus(self, status: str): + """ + Gets the string representation for the status of this match + """ + # LiveTime only exists if the status is live + # Avoids KeyErrors + if status.lower() == "live": + # Half time + if Navigation.LiveMatchPhase.value in self.matchDict and \ + Navigation.LiveMatchPhase.value == Navigation.HalfTime.value: + return Status.HalfTime.value + + # Current time + return self.matchDict[Navigation.LiveTime.value] + + # If no special status, pull it out of this dict + statusses: dict = { + "after_today": Status.AfterToday.value, + "not_started": Status.NotStarted.value, + "end": Status.Over.value + } + + return statusses[status.lower()] + + def _getWeekday(self): + """ + Gets the day of the week this match is played on + """ + day = self.start.weekday() + days = ["Ma", "Di", "Wo", "Do", "Vr", "Za", "Zo"] + return days[day] + + def getInfo(self): + """ + Returns a list of all the info of this class in order to create a table + """ + return [self.weekDay, self.date, self.home, self._getScore(), self.away, self.status] + + def _getScore(self): + """ + Returns a string representing the scoreboard + """ + # No score to show yet, show time when the match starts + if not self._hasStarted(): + return "{}:{}".format(leadingZero(str(self.start.hour)), leadingZero(str(self.start.minute))) + + return "{} - {}".format(self.homeScore, self.awayScore) + + def _hasStarted(self): + return self.status not in [Status.AfterToday.value, Status.NotStarted.value] + + +class Navigation(Enum): + """ + Enum to navigate through the matchdict, + seeing as the API is private the keys of the dict could change every now and then + so this makes sure a key only has to be changed once. + """ + AwayTeam = "awayTeam" + HomeTeam = "homeTeam" + AwayScore = "awayScore" + HomeScore = "homeScore" + LiveTime = "liveTime" + LiveMatchPhase = "liveMatchPhase" + HalfTime = "HALF_TIME" + Status = "status" + Name = "name" + + +def getMatches(matchweek: int): + """ + Function that constructs the table for a given matchweek + """ + current_day = getJPLMatches(matchweek) + + # API request failed + if current_day is None: + return "Er ging iets mis. Probeer het later opnieuw." + + matches = list(map(Match, current_day)) + matches = list(map(lambda x: x.getInfo(), matches)) + + header = "Jupiler Pro League - Speeldag {}".format(matchweek) + table = tabulate.tabulate(matches, headers=["Dag", "Datum", "Thuis", "Stand", "Uit", "Tijd"]) + + return "```{}\n\n{}```".format(header, table) diff --git a/functions/scraping.py b/functions/scraping.py index aef771a..a389bdd 100644 --- a/functions/scraping.py +++ b/functions/scraping.py @@ -76,3 +76,16 @@ def getMatchweek(): # "Speeldag DD" -> split on space & take second return match[0].split(" ")[1] + + +def getJPLMatches(week: int): + """ + JPL matches for a given matchweek + """ + current_day = get("https://api.sporza.be/web/soccer/matchdays/161733/{}".format(week)) + + # Something went wrong + if current_day.status_code != 200: + return None + + return current_day.json()["groupedMatches"][0]["matches"]