Пример #1
0
    def get_bets(league: str, season: int, matchday: int):
        """
        Displays all matches for a matchday with entries for betting
        :param league: The league to display
        :param season: The season to display
        :param matchday: The matchday to display
        :return: The response
        """
        validated = validate_matchday(league, season, matchday)
        if validated is None:
            return abort(404)
        league, season, matchday = validated

        matches: List[Match] = Match.query.filter_by(
            matchday=matchday, season=season,
            league=league).options(db.joinedload(Match.home_team),
                                   db.joinedload(Match.away_team)).all()
        if len(matches) == 0:
            flash("Den angegebenen Spieltag gibt es nicht", "danger")
            return redirect(url_for("bets.get_bets"))
        matches.sort(key=lambda x: x.kickoff)
        has_started = matches[0].has_started
        all_started = matches[-1].has_started

        bets = Bet.query.filter_by(matchday=matchday,
                                   season=season,
                                   league=league,
                                   user_id=current_user.id).all()

        matchday_points = 0
        for bet in bets:
            if bet.points is not None:
                matchday_points += bet.points

        bet_match_map = {(x.home_team_abbreviation, x.away_team_abbreviation):
                         x
                         for x in bets}

        bet_infos = []
        for match_item in matches:
            index = (match_item.home_team_abbreviation,
                     match_item.away_team_abbreviation)
            bet_infos.append((match_item, bet_match_map.get(index)))

        leaderboard = None
        if has_started:
            leaderboard = Leaderboard(
                league, season, matchday,
                DisplayBotsSettings.get_state(current_user))

        return render_template("betting/bets.html",
                               matchday=matchday,
                               season=season,
                               league=league,
                               bet_infos=bet_infos,
                               matchday_points=matchday_points,
                               has_started=has_started,
                               all_started=all_started,
                               leaderboard=leaderboard)
Пример #2
0
    def from_db(cls, user: User, list_name: str, service: ListService,
                media_type: MediaType, media_subtype: Optional[MediaSubType],
                minimum_diff: int, include_complete: bool):
        """
        Generates UpdateWrapper objects based on a couple of parameters
        and the current database contents
        :param user: The user for whom to load the updates
        :param list_name: The list name for which to load the updates
        :param service: The service for which to load the updates
        :param media_type: The media type for which to load the updates
        :param media_subtype: If specified, limits the results to a specific
                              media subtype (example: Light novels)
        :param minimum_diff: Specifies a minimum diff value
        :param include_complete: Specifies whether completed items should be
                                 included
        :return: A list of UpdateWrapper objects
        """
        media_lists: List[MediaList] = MediaList.query \
            .filter_by(
                user=user,
                name=list_name,
                service=service,
                media_type=media_type
            ) \
            .options(
                db.joinedload(MediaList.media_list_items)
                  .subqueryload(MediaListItem.media_user_state)
                  .subqueryload(MediaUserState.media_id)
                  .subqueryload(MediaId.chapter_guess)
            ) \
            .options(
                db.joinedload(MediaList.media_list_items)
                  .subqueryload(MediaListItem.media_user_state)
                  .subqueryload(MediaUserState.media_id)
                  .subqueryload(MediaId.media_item)
                  .subqueryload(MediaItem.media_ids)
            ) \
            .options(
                db.joinedload(MediaList.media_list_items)
                  .subqueryload(MediaListItem.media_user_state)
                  .subqueryload(MediaUserState.media_id)
                  .subqueryload(MediaId.media_item)
                  .subqueryload(MediaItem.ln_releases)
            ) \
            .all()

        return cls.from_media_lists(media_lists, media_subtype, minimum_diff,
                                    include_complete)
Пример #3
0
    def media(media_item_id: int):
        """
        Displays information on a media item
        :param media_item_id: The ID of the media item
        :return: None
        """
        media_item: MediaItem = MediaItem.query\
            .options(db.joinedload(MediaItem.media_ids)
                     .subqueryload(MediaId.media_user_states)
                     ).filter_by(id=media_item_id).first()

        if media_item is None:
            abort(404)

        if current_user is not None:
            media_user_states = []
            for media_id in media_item.media_ids:
                user_states = [
                    x for x in media_id.media_user_states
                    if x.user_id == current_user.id
                ]
                media_user_states += user_states
            media_user_states.sort(key=lambda x: x.media_id.service.value)
        else:
            media_user_states = []

        return render_template(
            "media/media.html",
            media_item=media_item,
            media_user_states=media_user_states
        )
    def load_history(cls, league: str, season: int, matchday: int) -> \
            List[Tuple[User, List["LeaderboardEntry"]]]:
        """
        Loads the history for the previous matches in a season for each user
        :param league: The league for which to retrieve the history
        :param season: The season for which to retrieve the history
        :param matchday: The matchday for which to retrieve the history
        :return: The history as a list of tuples of users and a list of
                 the corresponding LeaderboardEntry objects.
                 Sorted by current position
        """
        entries: List[LeaderboardEntry] = [
            x for x in LeaderboardEntry.query.filter_by(
                league=league, season=season).options(
                    db.joinedload(LeaderboardEntry.user)).all()
            if x.matchday <= matchday
        ]
        entries.sort(key=lambda x: x.matchday)

        history_dict: Dict[int, List[LeaderboardEntry]] = {}
        for entry in entries:
            if entry.user_id not in history_dict:
                history_dict[entry.user_id] = []
            history_dict[entry.user_id].append(entry)

        history_list = [(history[-1].user, history)
                        for _, history in history_dict.items()]
        history_list.sort(key=lambda x: x[1][-1].position)
        return history_list
Пример #5
0
    def match(league: str, season: int, matchday: int, matchup: str):
        """
        Displays a single match
        :param league: The league of the match
        :param season: The season of the match
        :param matchday: The matchday of the match
        :param matchup: The matchup string ('hometeam_awayteam')
        :return: The Response
        """
        try:
            home, away = matchup.split("_")
            match_item = Match.query.filter_by(
                league=league,
                season=season,
                matchday=matchday,
                home_team_abbreviation=home,
                away_team_abbreviation=away
            ).options(db.joinedload(Match.goals)).first()
            if match_item is None:
                raise ValueError()
        except ValueError:
            return abort(404)

        bets: List[Bet] = Bet.query.filter_by(
            league=league,
            season=season,
            matchday=matchday,
            home_team_abbreviation=home,
            away_team_abbreviation=away
        ).options(db.joinedload(Bet.user)
                  .subqueryload(User.profile)
                  .subqueryload(UserProfile.favourite_team)).all()
        if not DisplayBotsSettings.get_state(current_user):
            bets = [
                x for x in bets
                if DisplayBotsSettings.bot_symbol() not in x.user.username
            ]
        bets.sort(key=lambda x: x.user_id)
        if match_item.has_started:
            bets.sort(key=lambda x: x.points, reverse=True)

        return render_template(
            "info/match.html",
            match=match_item,
            bets=bets
        )
Пример #6
0
def update_bet_points():
    """
    Updates the bet points
    :return: None
    """
    bets = Bet.query.options(db.joinedload(Bet.match)).all()
    for bet in bets:
        bet.points = bet.evaluate()
    db.session.commit()
Пример #7
0
def send_new_update_notifications():
    """
    Sends out telegram notifications for media updates
    :return: None
    """
    start = time.time()
    app.logger.info("Starting check for notifications")

    user_states: List[MediaUserState] = MediaUserState.query\
        .options(
            db.joinedload(MediaUserState.media_id)
              .subqueryload(MediaId.media_item)
              .subqueryload(MediaItem.media_ids)
        ) \
        .options(
            db.joinedload(MediaUserState.media_id)
              .subqueryload(MediaId.chapter_guess)
        ) \
        .options(
            db.joinedload(MediaUserState.user)
              .subqueryload(User.telegram_chat_id)
        ) \
        .options(db.joinedload(MediaUserState.media_notification)) \
        .all()

    notification_settings: Dict[int, NotificationSetting] = {
        x.user_id: x
        for x in NotificationSetting.query.all()
    }

    for user_state in user_states:

        settings = notification_settings.get(user_state.user_id)
        if settings is None or not settings.value:
            continue
        else:
            handle_notification(user_state, settings)

    db.session.commit()
    app.logger.info(f"Completed check for notifications in "
                    f"{time.time() - start}s.")
Пример #8
0
def update_manga_chapter_guesses():
    """
    Updates the manga chapter guesses
    :return: None
    """
    start = time.time()
    app.logger.info("Starting update of manga chapter guesses")

    anilist_ids: List[MediaId] = MediaId.query\
        .filter_by(
            media_type=MediaType.MANGA,
            media_subtype=MediaSubType.MANGA,
            service=ListService.ANILIST
        ) \
        .options(db.joinedload(MediaId.media_user_states)) \
        .options(db.joinedload(MediaId.media_item)) \
        .options(db.joinedload(MediaId.chapter_guess)) \
        .all()

    for anilist_id in anilist_ids:
        if len(anilist_id.media_user_states) == 0:
            # We only update chapter guesses for items that are actively used
            # by users, since each update requires an HTTP request to anilist.
            # We don't want to be rate limited.
            continue
        elif anilist_id.chapter_guess is None:
            new_guess = MangaChapterGuess(media_id=anilist_id)
            db.session.add(new_guess)
            db.session.commit()
            guess = new_guess
        else:
            guess = anilist_id.chapter_guess

        app.logger.debug(f"Updating chapter guess for {anilist_id.service_id}")
        guess.update_guess()
        db.session.commit()

    db.session.commit()
    app.logger.info(f"Finished updating manga chapter guesses "
                    f"in {time.time() - start}")
Пример #9
0
    def team(team_abbreviation: int):
        """
        Displays information about a single team
        :param team_abbreviation: The ID of the team to display
        :return: The response
        """
        team_info = Team.query.filter_by(
            abbreviation=team_abbreviation
        ).options(db.joinedload(Team.players)
                  .subqueryload(Player.goals)).first()
        if team_info is None:
            return abort(404)

        recent_matches = [x for x in team_info.matches if x.finished]
        recent_matches.sort(key=lambda x: x.kickoff)
        recent_matches = recent_matches[-7:]

        match_data = []
        for match_item in recent_matches:
            if match_item.home_team_abbreviation == team_abbreviation:
                opponent = match_item.away_team
                own_score = match_item.home_current_score
                opponent_score = match_item.away_current_score
            else:
                opponent = match_item.home_team
                own_score = match_item.away_current_score
                opponent_score = match_item.home_current_score

            if own_score > opponent_score:
                result = "win"
            elif own_score < opponent_score:
                result = "loss"
            else:
                result = "draw"

            score = "{}:{}".format(own_score, opponent_score)
            match_data.append((
                match_item, opponent, score, result
            ))

        goal_data = []
        for player in team_info.players:
            goals = [x for x in player.goals if not x.own_goal]
            goal_data.append((player.name, len(goals)))
        goal_data.sort(key=lambda x: x[1], reverse=True)

        return render_template(
            "info/team.html",
            team=team_info,
            goals=goal_data,
            matches=match_data
        )
Пример #10
0
    def __init__(self, league: str, season: int, matchday: int,
                 include_bots: bool):
        """
        Initializes the leaerboard object
        :param league: The league of the leaderboard
        :param season: The season of the leaderboard
        :param matchday: The matchday of the leaderboard
        :param include_bots: Whether or not to include bots
        """
        self.league = league
        self.season = season
        self.matchday = matchday
        self.include_bots = include_bots

        self.season_winners: Dict[int, List[str]] = {}
        for season_winner in SeasonWinner.query.filter_by(league=league).all():
            user_id = season_winner.user_id
            winner_season = season_winner.season_string
            if user_id not in self.season_winners:
                self.season_winners[user_id] = []
            self.season_winners[user_id].append(winner_season)
        self.matchday_winners: Dict[int, List[int]] = {}
        for matchday_winner in MatchdayWinner.query.filter_by(
                league=league, season=season).all():
            user_id = matchday_winner.user_id
            winner_matchday = matchday_winner.matchday
            if user_id not in self.matchday_winners:
                self.matchday_winners[user_id] = []
            self.matchday_winners[user_id].append(winner_matchday)

        self.ranking: List[LeaderboardEntry] = \
            LeaderboardEntry.query.filter_by(
                league=league,
                season=season,
                matchday=matchday
            ).options(db.joinedload(LeaderboardEntry.user)
                      .subqueryload(User.profile)
                      .subqueryload(UserProfile.favourite_team)).all()
        self.ranking.sort(key=lambda x: x.position)
        self.history: List[Tuple[User, List["LeaderboardEntry"]]] \
            = LeaderboardEntry.load_history(league, season, matchday)

        if not include_bots:
            self.ranking = [
                x for x in self.ranking
                if DisplayBotsSettings.bot_symbol() not in x.user.username
            ]
            self.history = [
                x for x in self.history
                if DisplayBotsSettings.bot_symbol() not in x[0].username
            ]
Пример #11
0
    def __init__(
            self,
            league: str,
            season: int,
            matchday: int,
            user: Optional[User]
    ):
        """
        Initializes the lague table object
        :param league: The league
        :param season: The season
        :param matchday: The matchday
        :param user: Optionally, a user. This will replace the real
                     results with the bets of the player
        """
        self.league = league
        self.season = season
        self.matchday = matchday
        self.user = user

        self.teams = Team.get_teams_for_season(league, season)
        self.matches = Match.query.filter_by(
            league=league, season=season
        ).filter(Match.matchday <= matchday).options(
            db.joinedload(Match.home_team),
            db.joinedload(Match.away_team)
        ).all()
        self.bets = []
        if self.user is not None:
            self.bets = [
                x for x in Bet.query.filter_by(
                    user_id=self.user.id,
                    league=league,
                    season=season
                ).options(db.joinedload(Bet.match)).all()
                if x.match.matchday <= matchday
            ]
Пример #12
0
    def anime_week():
        """
        Shows the seasonal anime schedule for a user's media entries
        :return: None
        """
        media_user_states: List[MediaUserState] = MediaUserState.query\
            .options(
                db.joinedload(MediaUserState.media_id)
                .subqueryload(MediaId.media_item)
            )\
            .filter_by(user_id=current_user.id)\
            .all()

        time_limit = datetime.fromtimestamp(time.time() + 7 * 24 * 60 * 60)
        media_user_states = [
            x for x in media_user_states
            if x.media_id.media_item.next_episode_airing_time is not None
            and time_limit > x.media_id.media_item.next_episode_datetime
        ]

        weekdays = {
            1: "Monday",
            2: "Tuesday",
            3: "Wednesday",
            4: "Thursday",
            5: "Friday",
            6: "Saturday",
            7: "Sunday"
        }
        entries_by_weekday = []

        for weekday, weekday_name in weekdays.items():
            entries = []
            for user_state in media_user_states:
                next_airing = \
                    user_state.media_id.media_item.next_episode_datetime
                if next_airing.isoweekday() == weekday:
                    entries.append(user_state)
            entries.sort(
                key=lambda x: x.media_id.media_item.next_episode_airing_time
            )
            entries_by_weekday.append((weekday_name, entries))

        return render_template(
            "schedule/anime_week.html",
            schedule=entries_by_weekday
        )
Пример #13
0
def create_categorized_matches() -> Dict[
    Tuple[str, int], Dict[int, List[Match]]
]:
    """
    Sorts matches into seasons and matchdars
    :return: The matches categorized like this:
                {(league, season): {matchday: [match, ...]}}
    """
    seasons: Dict[Tuple[str, int], Dict[int, List[Match]]] = {}
    for match in Match.query.options(
            db.joinedload(Match.bets).subqueryload(Bet.user)
    ).all():
        league_season = (match.league, match.season)
        matchday = match.matchday
        if league_season not in seasons:
            seasons[league_season] = {}
        if matchday not in seasons[league_season]:
            seasons[league_season][matchday] = []
        seasons[league_season][matchday].append(match)
    return seasons
Пример #14
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
Пример #15
0
def do_media_item_inserts():
    """
    Preforms media item insertions
    :return: None
    """
    # Prepare existing DB content
    media_items: Dict[Tuple, MediaItem] = {
        x.identifier_tuple: x for x in
        MediaItem.query.all()
    }
    media_ids: List[MediaId] = MediaId.query\
        .options(db.joinedload(MediaId.media_item)) \
        .options(db.joinedload(MediaId.media_user_states)) \
        .all()
    media_service_items: \
        Dict[ListService, Dict[MediaType, Dict[str, MediaId]]] = \
        {x: {y: {} for y in MediaType} for x in ListService}
    for media_id in media_ids:
        service = media_id.service
        media_type = media_id.media_item.media_type
        service_id = media_id.service_id
        media_service_items[service][media_type][service_id] = media_id
    media_lists: Dict[Tuple, MediaList] = {
        x.identifier_tuple: x for x in
        MediaList.query.options(db.joinedload(MediaList.media_list_items))
        .all()
    }

    # Add media list items, ids and user states
    while len(media_item_queue) > 0:
        queued = media_item_queue.pop(0)
        params, service, service_ids, user_state_params, list_params = queued

        service_id = service_ids.get(service)
        if service_id is None:
            app.logger.error(f"Missing service ID for service {service}")

        media_type = params["media_type"]

        app.logger.debug(f"Inserting {params['romaji_title']} into database")

        if service_id in media_service_items[service]:
            media_id = media_service_items[service][media_type][service_id]
            media_item = media_id.media_item
            media_item.update(MediaItem(**params))
        else:
            generated = MediaItem(**params)
            identifier = generated.identifier_tuple
            existing = media_items.get(identifier)
            if existing is None:
                media_item = generated
                db.session.add(media_item)
                media_items[identifier] = media_item
            else:
                media_item = existing
                media_item.update(generated)

        # Insert Media IDs
        for extra_service, extra_id in service_ids.items():

            associated = media_service_items[extra_service][media_type].get(
                extra_id
            )

            if associated is None:
                media_id = MediaId(
                    media_item=media_item, service=extra_service,
                    service_id=extra_id
                )
                db.session.add(media_id)
                db.session.commit()
                media_service_items[extra_service][media_type][extra_id] \
                    = media_id
            elif associated.media_item.id != media_item.id:
                associated.media_item_id = media_item.id

        # Insert User States
        if user_state_params is not None:
            media_id = media_service_items[service][media_type][service_id]
            user_state_params["media_id_id"] = media_id.id
            generated = MediaUserState(**user_state_params)
            existing_user_states = {
                x.user_id: x for x in media_id.media_user_states
            }
            user_id = user_state_params["user_id"]
            existing_user_state = existing_user_states.get(user_id)

            if existing_user_state is None:
                db.session.add(generated)
                user_state = generated
                db.session.commit()
            else:
                existing_user_state.update(generated)
                user_state = existing_user_state

            if list_params is not None:
                generated = MediaList(**list_params)
                identifier = generated.identifier_tuple
                existing_list = media_lists.get(identifier)

                if existing_list is None:
                    db.session.add(generated)
                    db.session.commit()
                    media_list = generated
                    media_lists[identifier] = media_list
                else:
                    existing_list.update(generated)
                    media_list = existing_list

                media_list_item_ids = [
                    x.media_user_state_id for x in media_list.media_list_items
                ]
                if user_state.id not in media_list_item_ids:
                    list_item = MediaListItem(
                        media_list_id=media_list.id,
                        media_user_state_id=user_state.id
                    )
                    db.session.add(list_item)