from typing import Optional from attr import dataclass, field from datetime import datetime from enum import Enum from functions.timeFormatters import fromString from functions.scrapers.sporza import getJPLMatches, getJPLTable from functions.stringFormatters import leading_zero import re from requests import get import tabulate class Status(Enum): AfterToday = "--:--" NotStarted = "--:--" Postponed = "--:--" 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: Optional[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._get_status(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._has_started(): self.homeScore = self.matchDict[Navigation.HomeScore.value] self.awayScore = self.matchDict[Navigation.AwayScore.value] if "startDateTime" in self.matchDict: self.start = fromString(self.matchDict["startDateTime"], formatString="%Y-%m-%dT%H:%M:%S.%f%z") else: self.start = None self.date = self.start.strftime("%d/%m") if self.start is not None else "Uitgesteld" self.weekDay = self._get_weekday() if self.start is not None else "??" def _get_status(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 \ self.matchDict[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, "postponed": Status.Postponed.value, "end": Status.Over.value } return statusses[status.lower()] def _get_weekday(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 get_info(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._get_score(), self.away, self.status] def _get_score(self): """ Returns a string representing the scoreboard """ if self.start is None: return "??" # No score to show yet, show time when the match starts if not self._has_started(): return "{}:{}".format(leading_zero(str(self.start.hour)), leading_zero(str(self.start.minute))) return "{} - {}".format(self.homeScore, self.awayScore) def _has_started(self): return self.status not in [Status.AfterToday.value, Status.NotStarted.value, Status.Postponed.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 get_matches(matchweek: int): """ Function that constructs the list of matches for a given matchweek """ current_day = getJPLMatches(matchweek) # API request failed if current_day is None: return "Er ging iets fout. Probeer het later opnieuw." matches = list(map(Match, current_day)) matches = list(map(lambda x: x.get_info(), 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) def get_table(): """ Function that constructs the current table of the JPL """ rows = getJPLTable() if rows is None: return "Er ging iets fout. Probeer het later opnieuw." # Format every row to work for Tabulate formatted = [_format_row(row) for row in rows] header = "Jupiler Pro League Klassement" table = tabulate.tabulate(formatted, headers=["#", "Ploeg", "Punten", "M", "M+", "M-", "M=", "D+", "D-", "D+/-"]) return "```{}\n\n{}```".format(header, table) def _format_row(row): """ Function that formats a row into a list for Tabulate to use """ tds = row.find_all("td") tds.pop(1) # Relegation icon tds.pop(1) # Copy of team name scoresArray = list([td.renderContents().decode("utf-8") for td in tds])[:9] # Insert the team name into the list scoresArray.insert(1, row.find_all("a")[0].renderContents().decode("utf-8").split("