Coverage for bundesliga_tippspiel/background/pointscalc.py: 8%
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""LICENSE
2Copyright 2017 Hermann Krumrey <hermann@krumreyh.com>
4This file is part of bundesliga-tippspiel.
6bundesliga-tippspiel is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 3 of the License, or
9(at your option) any later version.
11bundesliga-tippspiel is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
16You should have received a copy of the GNU General Public License
17along with bundesliga-tippspiel. If not, see <http://www.gnu.org/licenses/>.
18LICENSE"""
20import time
21from jerrycan.base import db, app
22from jerrycan.db.User import User
23from typing import List, Dict, Tuple
24from bundesliga_tippspiel.db import Bet, Match, LeaderboardEntry, \
25 DisplayBotsSettings, MatchdayWinner
26from bundesliga_tippspiel.utils.matchday import get_matchday_info
29def update_leaderboard():
30 """
31 Updates the leaderboard entries
32 :return: None
33 """
34 start = time.time()
35 app.logger.info("Updating leaderboard entries")
36 update_bet_points()
37 users = User.query.filter_by(confirmed=True).all()
38 seasons = create_categorized_matches()
39 season_points = calculate_matchday_points(users, seasons)
41 process_matchday_winners(season_points)
43 for (league, season), user_points in season_points.items():
45 previous_positions = {}
46 previous_no_bot_positions = {}
47 user_totals = {user: 0 for user in users}
48 for matchday, matchday_points in sorted(
49 user_points.items(), key=lambda x: x[0]
50 ):
51 for user, points in matchday_points.items():
52 user_totals[user] += points
53 process_league_table(
54 league,
55 season,
56 matchday,
57 user_totals,
58 previous_positions,
59 previous_no_bot_positions
60 )
62 app.logger.debug(f"Finished leaderboard update in "
63 f"{time.time()-start:.2f}s")
66def update_bet_points():
67 """
68 Updates the bet points
69 :return: None
70 """
71 bets = Bet.query.options(db.joinedload(Bet.match)).all()
72 for bet in bets:
73 bet.points = bet.evaluate()
74 db.session.commit()
77def create_categorized_matches() -> Dict[
78 Tuple[str, int], Dict[int, List[Match]]
79]:
80 """
81 Sorts matches into seasons and matchdays
82 :return: The matches categorized like this:
83 {(league, season): {matchday: [match, ...]}}
84 """
85 seasons: Dict[Tuple[str, int], Dict[int, List[Match]]] = {}
86 for match in Match.query.options(
87 db.joinedload(Match.bets).subqueryload(Bet.user)
88 ).all():
89 league_season = (match.league, match.season)
90 matchday = match.matchday
91 if league_season not in seasons:
92 seasons[league_season] = {}
93 if matchday not in seasons[league_season]:
94 seasons[league_season][matchday] = []
95 seasons[league_season][matchday].append(match)
96 return seasons
99def calculate_matchday_points(
100 users: List[User],
101 seasons: Dict[Tuple[str, int], Dict[int, List[Match]]]
102) -> Dict[Tuple[str, int], Dict[int, Dict[User, int]]]:
103 """
104 Calculates every user's points per matchday for each season
105 :param users: The users to include
106 :param seasons: The seasons data
107 :return: {(league, season): {matchday: {user: points}}}
108 """
109 season_points = {}
110 for league_season, season_data in seasons.items():
112 user_points = {
113 matchday: {user: 0 for user in users}
114 for matchday in season_data.keys()
115 }
117 for matchday, matches in sorted(
118 season_data.items(), key=lambda x: x[0]
119 ):
120 for match in matches:
121 for bet in match.bets:
122 if bet.points is not None:
123 user_points[matchday][bet.user] += bet.points
125 season_points[league_season] = user_points
126 return season_points
129def process_matchday_winners(
130 per_matchday_data: Dict[Tuple[str, int], Dict[int, Dict[User, int]]]
131):
132 """
133 Processes the matchday winners
134 :param per_matchday_data: The points of each user per matchday
135 :return: None
136 """
137 for (league, season), matchday_info in per_matchday_data.items():
138 current_matchday = get_matchday_info(league, season)[0]
139 for matchday, user_points in matchday_info.items():
140 if matchday > current_matchday:
141 continue
143 best_user_id, best_points = None, 0
144 for user, points in user_points.items():
146 if DisplayBotsSettings.bot_symbol() in user.username:
147 continue
148 if points > best_points:
149 best_user_id, best_points = user.id, points
150 elif points == best_points:
151 best_user_id = None
153 matchday_winner = MatchdayWinner(
154 league=league,
155 season=season,
156 matchday=matchday,
157 user_id=best_user_id
158 )
159 db.session.merge(matchday_winner)
160 db.session.commit()
163def process_league_table(
164 league: str,
165 season: int,
166 matchday: int,
167 user_points: Dict[User, int],
168 previous_positions: Dict[User, int],
169 previous_no_bot_positions: Dict[User, int]
170):
171 """
172 Processes the league table entries and updates their corresponding database
173 entries.
174 :param league: The league to process
175 :param season: The season to process
176 :param matchday: The matchday to process
177 :param user_points: The points for every user to process:
178 :param previous_positions: Dictionary that keeps track of the previous
179 positions of the users
180 :param previous_no_bot_positions: Dictionary that keeps track of the
181 previous positions of the users
182 disregarding bots
183 :return: None
184 """
185 bot_symbol = DisplayBotsSettings.bot_symbol()
186 ranking = [(user, points) for user, points in user_points.items()]
187 ranking.sort(key=lambda x: x[0].id)
188 ranking.sort(key=lambda x: x[1], reverse=True)
190 no_bot_ranking = [x for x in ranking if bot_symbol not in x[0].username]
191 no_bot_positions = {}
192 for i, (user, _) in enumerate(no_bot_ranking):
193 position = i + 1
194 no_bot_positions[user] = position
196 for i, (user, points) in enumerate(ranking):
197 position = i + 1
198 no_bot_position = no_bot_positions.get(user, position)
200 previous_position = previous_positions.get(user, position)
201 previous_positions[user] = position
202 no_bot_previous_position = previous_no_bot_positions.get(
203 user, no_bot_position
204 )
205 previous_no_bot_positions[user] = no_bot_position
207 entry = LeaderboardEntry(
208 league=league,
209 season=season,
210 matchday=matchday,
211 user_id=user.id,
212 points=points,
213 position=position,
214 no_bot_position=no_bot_position,
215 previous_position=previous_position,
216 no_bot_previous_position=no_bot_previous_position
217 )
218 db.session.merge(entry)
219 db.session.commit()