]> git.xonotic.org Git - xonotic/xonstat.git/blob - xonstat/models/player.py
Change to explicit floats, add string representations of objects.
[xonotic/xonstat.git] / xonstat / models / player.py
1 """
2 Models related to players.
3 """
4
5 from calendar import timegm
6
7 from xonstat.models.mixins import FuzzyDateMixin, EpochMixin, NickColorsMixin
8 from xonstat.util import strip_colors, pretty_date, qfont_decode
9
10 # Glicko Constants
11
12 # the default initial rating value
13 MU = 1500.0
14
15 # the default ratings deviation value
16 PHI = 350.0
17
18 # the default volatility value
19 SIGMA = 0.06
20
21 # the ratio to convert from/to glicko2
22 GLICKO2_SCALE = 173.7178
23
24
25 class Player(EpochMixin, NickColorsMixin, FuzzyDateMixin):
26     """
27     A player, which can represent either a human or a bot.
28     """
29
30     def nick_strip_colors(self):
31         if self.nick is None:
32             return "Anonymous Player"
33         else:
34             return strip_colors(self.nick)
35
36     def __repr__(self):
37         return "<Player({}, {})>".format(self.player_id, self.nick.encode('utf-8'))
38
39     def to_dict(self):
40         return {
41             'player_id': self.player_id,
42             'nick': self.nick,
43             'joined': self.create_dt.strftime('%Y-%m-%dT%H:%M:%SZ'),
44             'active_ind': self.active_ind,
45             'location': self.location,
46             'stripped_nick': qfont_decode(self.stripped_nick),
47         }
48
49
50 class Achievement(object):
51     """
52     A type of achievement. Referenced implicitly in PlayerAchievement.
53     """
54
55     def __repr__(self):
56         return "<Achievement({0.achievement_cd}, {0.descr}, {0.limit})>".format(self)
57
58     def to_dict(self):
59         return {
60             'achievement_cd': self.achievement_cd,
61             'name': self.descr,
62             'limit':self.limit,
63         }
64
65
66 class PlayerAchievement(object):
67     """
68     Achievements a player has earned.
69     """
70
71     def __repr__(self):
72         return "<PlayerAchievement({0.player_id}, {0.achievement_cd})>".format(self)
73
74     def to_dict(self):
75         return {
76             'player_id': self.player_id,
77             'achievement_cd': self.achievement_cd,
78         }
79
80
81 class Hashkey(object):
82     """
83     A player's identifying key from the d0_blind_id library.
84     """
85
86     def __init__(self, player_id=None, hashkey=None):
87         self.player_id = player_id
88         self.hashkey = hashkey
89
90     def __repr__(self):
91         return "<Hashkey({0.player_id}, {0.hashkey})>".format(self)
92
93     def to_dict(self):
94         return {
95             'player_id': self.player_id,
96             'hashkey': self.hashkey
97         }
98
99
100 class PlayerNick(object):
101     """
102     A single nickname a player has used in a game.
103     """
104
105     def __repr__(self):
106         return "<PlayerNick({0.player_id}, {0.stripped_nick})>".format(self)
107
108     def to_dict(self):
109         return {
110             'player_id': self.player_id,
111             'name': qfont_decode(self.stripped_nick),
112         }
113
114
115 class PlayerElo(object):
116     """
117     A player's skill for a particular game type, as determined by a modified Elo algorithm.
118     """
119
120     def __init__(self, player_id=None, game_type_cd=None, elo=None):
121         self.player_id = player_id
122         self.game_type_cd = game_type_cd
123         self.elo = elo
124         self.score = 0
125         self.games = 0
126
127     def __repr__(self):
128         return ("<PlayerElo(pid={0.player_id}, gametype={0.game_type_cd}, elo={0.elo}, "
129                 "games={0.games})>".format(self))
130
131     def to_dict(self):
132         return {
133             'player_id': self.player_id,
134             'game_type_cd': self.game_type_cd,
135             'elo': self.elo,
136             'games': self.games,
137         }
138
139
140 class PlayerRank(NickColorsMixin):
141     """
142     A player's rank for a given game type.
143     """
144
145     def __repr__(self):
146         return ("<PlayerRank(pid={0.player_id}, gametype={0.game_type_cd}, rank={0.rank})>"
147                 .format(self))
148
149     def to_dict(self):
150         return {
151             'player_id': self.player_id,
152             'game_type_cd': self.game_type_cd,
153             'rank': self.rank
154         }
155
156
157 class PlayerCaptime(FuzzyDateMixin, EpochMixin):
158     """
159     A flag capture time for a player on a given map.
160     """
161
162     def __init__(self, player_id=None, game_id=None, map_id=None, fastest_cap=None, mod=None):
163         self.player_id = player_id
164         self.game_id = game_id
165         self.map_id = map_id
166         self.fastest_cap = fastest_cap
167         self.mod = mod
168
169     def __repr__(self):
170         return "<PlayerCaptime(pid={0.player_id}, map_id={0.map_id}, mod={0.mod})>".format(self)
171
172
173 class PlayerGroups(object):
174     """
175     An authorization group a player belongs to. Used to control access.
176     """
177
178     def __init__(self, player_id=None, group_name=None):
179         self.player_id  = player_id
180         self.group_name = group_name
181
182     def __repr__(self):
183         return "<PlayerGroups({0.player_id}, {0.group_name})>".format(self)
184
185
186 # TODO: determine if this is a real model (it is very similar to PlayerCaptime from above)
187 class PlayerCapTime(object):
188     """
189     Fastest flag capture times per player.
190     """
191
192     def __init__(self, row):
193         self.fastest_cap = row.fastest_cap
194         self.create_dt = row.create_dt
195         self.create_dt_epoch = timegm(row.create_dt.timetuple())
196         self.create_dt_fuzzy = pretty_date(row.create_dt)
197         self.player_id = row.player_id
198         self.game_id = row.game_id
199         self.map_id = row.map_id
200         self.map_name = row.map_name
201         self.server_id = row.server_id
202         self.server_name = row.server_name
203
204     def to_dict(self):
205         return {
206             "fastest_cap" : self.fastest_cap.total_seconds(),
207             "create_dt_epoch": self.create_dt_epoch,
208             "create_dt_fuzzy": self.create_dt_fuzzy,
209             "game_id":self.game_id,
210             "map_id": self.map_id,
211             "map_name": self.map_name,
212             "server_id": self.server_id,
213             "server_name": self.server_name,
214         }
215
216
217 class PlayerMedal(object):
218     """
219     A medal a player has earned in a large tournament.
220     """
221
222     def __repr__(self):
223         return "<PlayerMedal(pid={0.player_id}, place={0.place}, alt={0.alt})>".format(self)
224
225
226 class PlayerGlicko(object):
227     """
228     A player's skill for a particular game type, as determined by the Glicko2 algorithm.
229     """
230     def __init__(self, player_id, game_type_cd, category="general", mu=MU, phi=PHI, sigma=SIGMA):
231         self.player_id = player_id
232         self.game_type_cd = game_type_cd
233         self.category = category
234         self.mu = float(mu)
235         self.phi = phi
236         self.sigma = sigma
237
238     def to_glicko2(self):
239         """ Convert a rating to the Glicko2 scale. """
240         return PlayerGlicko(
241             player_id=self.player_id,
242             game_type_cd=self.game_type_cd,
243             category=self.category,
244             mu=(float(self.mu) - MU)/GLICKO2_SCALE,
245             phi=self.phi/GLICKO2_SCALE,
246             sigma=self.sigma
247         )
248
249     def from_glicko2(self):
250         """ Convert a rating to the original Glicko scale. """
251         return PlayerGlicko(
252             player_id=self.player_id,
253             game_type_cd=self.game_type_cd,
254             category=self.category,
255             mu=self.mu * GLICKO2_SCALE + MU,
256             phi=self.phi * GLICKO2_SCALE,
257             sigma=self.sigma
258         )
259
260     def __repr__(self):
261         return ("<PlayerGlicko({0.player_id}, {0.game_type_cd}, {0.category}, "
262                 "{0.mu}, {0.phi}, {0.sigma})>".format(self))
263
264
265 class PlayerGlickoBase(PlayerGlicko):
266     """
267     A clone of the above PlayerGlicko class, but created separately in order to avoid
268     dealing with primary and non-primary SQLAlchemy mappers.
269     """
270     pass