def get_selected_league() -> Tuple[str, int]:
    """
    :return: The league currently selected by the user by means of cookies
    """
    league = str(request.cookies.get("league", Config.OPENLIGADB_LEAGUE))
    try:
        season = int(request.cookies.get("season", Config.season()))
    except ValueError:
        season = Config.season()
    return league, season
def validate_matchday(
        league: Optional[str],
        season: Optional[int],
        matchday: Optional[int]
) -> Optional[Tuple[str, int, int]]:
    """
    Performs checks that a league/season/matchday combination is valid.
    Can also fill these values with default values
    :param league: The league to check
    :param season: The season to check
    :param matchday: The matchday to check
    :return: league, season, matchday if valid, None if invalid
    """
    try:
        default_league, default_season = get_selected_league()
    except RuntimeError:
        default_league, default_season = \
            Config.OPENLIGADB_LEAGUE, Config.season()

    if league is None:
        league = default_league
    if season is None:
        season = default_season
    current_matchday, max_matchday = get_matchday_info(league, season)
    if matchday is None:
        matchday = current_matchday

    if not 1 <= matchday <= max_matchday:
        return None
    else:
        return league, season, matchday
def load_season_events() -> List[SeasonEvent]:
    """
    Loads all event states from the database
    :return: The event states
    """
    existing = {
        x.event_type: x
        for x in SeasonEvent.query.filter_by(
            season=Config.season(), league=Config.OPENLIGADB_LEAGUE).all()
    }
    for event_type in SeasonEventType:
        if event_type not in existing:
            new = SeasonEvent(event_type=event_type,
                              executed=False,
                              season=Config.season(),
                              league=Config.OPENLIGADB_LEAGUE)
            db.session.add(new)
            existing[event_type] = new

    db.session.commit()
    return list(existing.values())
Example #4
0
    def get_due_matches(self) -> List[Match]:
        """
        Checks if the reminder is due and returns a list of matches that the
        user still needs to bet on.
        :return: The matches for which the reminder is due
        """
        now = datetime.utcnow()
        start = max(now, self.last_reminder_datetime)
        start_str = start.strftime("%Y-%m-%d:%H-%M-%S")
        then = now + self.reminder_time_delta
        then_str = then.strftime("%Y-%m-%d:%H-%M-%S")

        due_matches: List[Match] = [
            x for x in Match.query.filter_by(
                season=Config.season(),
                league=Config.OPENLIGADB_LEAGUE
            ).all()
            if start_str < x.kickoff < then_str
        ]
        user_bet_matches = [
            (bet.match.home_team_abbreviation,
             bet.match.away_team_abbreviation,
             bet.match.season)
            for bet in Bet.query.filter_by(
                user_id=self.user_id,
                season=Config.season(),
                league=Config.OPENLIGADB_LEAGUE
            ).options(db.joinedload(Bet.match)).all()
        ]
        to_remind = []
        for match in due_matches:
            identifier = (match.home_team_abbreviation,
                          match.away_team_abbreviation,
                          match.season)
            if identifier not in user_bet_matches:
                to_remind.append(match)

        return to_remind
def update_openligadb():
    """
    Updates all OpenLigaDB leagues in the configuration
    :return: None
    """
    start = time.time()
    app.logger.debug("Updating OpenLigaDB data")
    for league, season in Config.all_leagues():
        update_required = UpdateTracker.update_required(league, season)
        if update_required:
            UpdateTracker.UPDATES[(league, season)] = int(time.time())
            update_match_data(league, str(season))
    app.logger.debug(
        f"Finished OpenLigaDB update in {time.time() - start:.2f}s")
    def update_required(league: str, season: int) -> bool:
        """
        Checks if a league requires updating
        :param league: The league to check
        :param season: The season to check
        :return: True if update required, False otherwise
        """
        league_tuple = (league, season)
        last_update = UpdateTracker.UPDATES.get(league_tuple, 0)

        if last_update == 0:
            return True  # Update on startup

        now = datetime.utcnow()
        delta = time.time() - last_update

        matches: List[Match] = Match.query.filter_by(season=season,
                                                     league=league).all()
        unfinished_matches = [x for x in matches if not x.finished]

        if len(matches) == 0:  # Initial filling of DB
            return True

        unfinished_matches.sort(key=lambda x: x.kickoff)
        # Don't update after the season ends
        if len(unfinished_matches) == 0:
            return False

        started_matches = [
            match for match in unfinished_matches if match.has_started
        ]
        is_primary = \
            league_tuple == (Config.OPENLIGADB_LEAGUE, Config.season())

        if delta > 60 * 60 * 24:  # Once a day minimum update
            return True
        elif is_primary and delta > 60 * 60:
            # Update Primary league once an hour
            return True
        elif len(started_matches) > 0:
            last_started_match = started_matches[-1]
            last_started_delta = last_started_match.kickoff_datetime - now
            last_started_seconds_delta = last_started_delta.seconds
            # Increase update frequency during and after matches
            if last_started_seconds_delta < 60 * 180:
                return True
            else:
                return False
        else:
            return False
def __handle_reminder(matchday: int, delta: timedelta, message_file: str,
                      before: bool) -> bool:
    """
    Handles sending out a reminder after/before a specified time
    :param matchday: The matchday that acts as an anchor
    :param delta: The time delta relative to the matchday
    :param message_file: The HTML template containing the message
    :param before: Whether or not the email should be sent before the event
    :return: True if the message was sent, False if it was not yet due
    """
    kickoff_times = [
        x.kickoff_datetime
        for x in Match.query.filter_by(matchday=matchday,
                                       season=Config.season(),
                                       league=Config.OPENLIGADB_LEAGUE).all()
    ]

    if len(kickoff_times) == 0:
        return False
    elif before:
        kickoff = min(kickoff_times)
    else:
        kickoff = max(kickoff_times)

    now = datetime.utcnow()

    if before and kickoff - delta > now:
        return False
    elif not before and kickoff + delta > now:
        return False
    else:
        for user in User.query.filter_by(confirmed=True).all():

            message = render_template(message_file, user=user)

            send_email(user.email,
                       f"Fußball Tippspiel Saison {Config.season_string()}",
                       message, Config.SMTP_HOST, Config.SMTP_ADDRESS,
                       Config.SMTP_PASSWORD, Config.SMTP_PORT)

            telegram = user.telegram_chat_id
            if telegram is not None:
                message = BeautifulSoup(message, "html.parser").text
                message = "\n".join([x.strip() for x in message.split("\n")])
                telegram.send_message(message)
        return True
Example #8
0
 def api_get_leagues():
     """
     Retrieves a list of available leagues
     :return: The list of available leagues
     """
     return {"leagues": Config.all_leagues()}
 def season_string(self) -> str:
     """
     :return: The season string, e.g. Bundesliga 2019/20
     """
     return Config.league_string(self.league, self.season)