1 from collections import OrderedDict
2 import csv
3 import functools
4
5 categories = ("passing", "rushing", "receiving",
6 "fumbles", "kicking", "punting", "kickret", "puntret",
7 "defense")
8 """
9 categories is a list of all individual statistical categories reported by
10 NFL's GameCenter.
11 """
12
14 """
15 Players implements a sequence type and provides a convenient API for
16 searching sets of players.
17 """
18
20 """
21 Creates a new Players sequence from an iterable where each element
22 of the iterable is an instance of the Player class.
23 """
24 self.__players = iterable
25
26
27
28
29
30 for category in categories:
31 def gen(category):
32 def _gen():
33 for p in self:
34 if p.__dict__[category]:
35 yield p
36 return lambda: Players(_gen())
37 self.__dict__['_%s' % category] = gen(category)
38
39 - def name(self, name):
40 """
41 Returns a single player whose name equals `name`. If no such player
42 can be found, None is returned.
43
44 Note that NFL GameCenter formats their names like "T.Brady" and
45 "W.Welker". Thus, `name` should also be in this format.
46 """
47 for p in self:
48 if p.name == name:
49 return p
50 return None
51
53 """
54 Returns a single player whose NFL GameCenter identifier equals
55 `playerid`. This probably isn't too useful, unless you need
56 to disambiguate between two players with the name first initial
57 and last name.
58
59 If no such player with the given identifier is found, None is
60 returned.
61 """
62 for p in self:
63 if p.playerid == playerid:
64 return p
65 return None
66
68 """
69 touchdowns is a convenience method for returning a Players
70 sequence of all players with at least one touchdown.
71
72 It is essentially a filter on the following fields: passing_tds,
73 rushing_tds, receiving_tds, kickret_tds and puntret_tds.
74 """
75 tdfields = ('passing_tds', 'rushing_tds', 'receiving_tds',
76 'kickret_tds', 'puntret_tds')
77 def gen():
78 for p in self:
79 for f in tdfields:
80 if f not in p.__dict__:
81 continue
82 if p.__dict__[f] > 0:
83 yield p
84 return Players(gen())
85
87 """
88 filters the Players sequence based on a set of criteria. Parameter
89 names should be equivalent to the properties accessible in instances
90 of the Player class.
91
92 For example::
93
94 players.filter(home=True, passing_tds=1, rushing_yds=lambda x: x>0)
95
96 Returns a Players sequence with only players on the home team that
97 have a single passing touchdown and more than zero rushing yards.
98
99 If a field specified does not exist for a particular Player, that
100 Player is excluded from the result set.
101
102 If a field is set to a value, then only players with fields that equal
103 that value are returned.
104
105 If a field is set to a function---which must be a predicate---then
106 only players with field values satisfying that function will
107 be returned.
108 """
109 preds = []
110 for k, v in kwargs.iteritems():
111 def pred(field, value, player):
112 if field not in player.__dict__:
113 return False
114 if isinstance(value, type(lambda x: x)):
115 return value(player.__dict__[field])
116 return player.__dict__[field] == value
117 preds.append(functools.partial(pred, k, v))
118 def gen():
119 for p in self:
120 if all([f(p) for f in preds]):
121 yield p
122 return Players(gen())
123
125 """Returns players that have a "passing" statistical category."""
126 return self._passing()
127
129 """Returns players that have a "rushing" statistical category."""
130 return self._rushing()
131
133 """Returns players that have a "receiving" statistical category."""
134 return self._receiving()
135
137 """Returns players that have a "fumbles" statistical category."""
138 return self._fumbles()
139
141 """Returns players that have a "kicking" statistical category."""
142 return self._kicking()
143
145 """Returns players that have a "punting" statistical category."""
146 return self._punting()
147
149 """Returns players that have a "kickret" statistical category."""
150 return self._kickret()
151
153 """Returns players that have a "puntret" statistical category."""
154 return self._puntret()
155
157 """Returns players that have a "kicking" statistical category."""
158 return self._kicking()
159
161 """
162 Limit the sequence to N players.
163 """
164 def gen():
165 for i, p in enumerate(self):
166 if i >= n:
167 return
168 yield p
169 return Players(gen())
170
171 - def sort(self, field, descending=True):
172 """
173 sorts the players according to the field specified---where field is
174 a property in a Player object. If descending is false, players will
175 be sorted in order from least to greatest.
176
177 Note that if field does not exist in any Player being sorted, a
178 KeyError will be raised.
179 """
180 return Players(sorted(self.__players, reverse=descending,
181 key=lambda p: p.__dict__[field]))
182
183 - def csv(self, csvfile):
184 """
185 Given a file-like object `csvfile`, csv will write the contents of
186 the Players sequence to csvfile formatted as comma-separated values.
187 The resulting file can then be opened directly with programs like
188 Excel, Google Docs, Libre Office and Open Office.
189
190 Note that since each player in a Players sequence may have differing
191 statistical categories (like a quarterback and a receiver), the
192 minimum constraining set of statisical categories is used as the
193 header row for the resulting CSV file.
194 """
195 fields, rows = [], []
196 players = list(self)
197 for p in players:
198 for category, stats in p.all_stats().iteritems():
199 for stat in stats:
200 field = '%s_%s' % (category, stat)
201 if field in fields:
202 continue
203 fields.append(field)
204 for p in players:
205 d = {
206 'name': p.name,
207 'id': p.playerid,
208 'home': p.home and 'yes' or 'no',
209 }
210 for field in fields:
211 if field in p.__dict__:
212 d[field] = p.__dict__[field]
213 else:
214 d[field] = ""
215 rows.append(d)
216
217 fieldNames = ["name", "id", "home"] + fields
218 rows = [{f: f for f in fieldNames}] + rows
219 csv.DictWriter(csvfile, fieldNames).writerows(rows)
220
222 """
223 Adds two Players sequences by concatenating the generators composing
224 each Players sequence.
225 """
226 if other is None:
227 return self
228 def gen():
229 for p in self:
230 yield p
231 for p in other:
232 yield p
233 return Players(gen())
234
236 """Returns a list of player names in the sequence."""
237 return '[%s]' % ', '.join([str(p) for p in self])
238
240 """Make this an iterable sequence."""
241 if self.__players is None:
242 return iter([])
243 if isinstance(self.__players, OrderedDict):
244 return self.__players.itervalues()
245 return iter(self.__players)
246
248 """Satisfy the built in reversed."""
249 return reversed(self.__players)
250
252 """
253 Player represents a single player and all of his statistical categories
254 for a single game. Every player has 'playerid', 'name' and 'home' fields.
255 Additionally, depending upon which statistical categories that player
256 was involved in for the game, he'll have properties such as 'passing_tds',
257 'rushing_yds', 'defense_int' and 'kicking_fgm'.
258
259 In order to know whether a paricular player belongs to a statical category,
260 you may use the filtering methods of a Players sequence or alternatively,
261 use the 'passing', 'rushing', 'kicking', etc., boolean members of this
262 class.
263
264 You may also inspect whether a player has a certain property by using
265 the special __dict__ attribute. For example::
266
267 if 'passing_yds' in player.__dict__:
268 # Do something with player.passing_yds
269 """
270 - def __init__(self, playerid, name, home):
271 """
272 Create a new Player instance with the player id (from NFL.com's
273 GameCenter), the player's name (e.g., "T.Brady") and whether the
274 player is playing in a home game or not.
275 """
276 self.playerid = playerid
277 self.name = name
278 self.home = home
279 self.__stats = OrderedDict()
280 for category in categories:
281 self.__dict__[category] = False
282
284 """
285 Returns a dict of all stats for the player. Each key is a statistical
286 category corresponding to a dict with keys corresponding to each
287 statistic and values corresponding to each statistic's value.
288 """
289 return self.__stats
290
300
302 assert category not in self.__stats, \
303 'Cannot add two sets of stats from the same category ' \
304 'to the same player "%s"' % self.name
305 self.__dict__[category] = True
306 for stat, val in stats.iteritems():
307 if stat == "name":
308 continue
309 self.__dict__['%s_%s' % (category, stat)] = val
310 self.__stats.setdefault(category, OrderedDict())[stat] = val
311
313 """
314 Simply returns the player's name, e.g., "T.Brady".
315 """
316 return self.name
317
319 """
320 Adds two players together. Only two player objects that correspond
321 to the same human (i.e., GameCenter identifier) can be added together.
322
323 If two different players are added together, an assertion will
324 be raised.
325
326 The effect of adding two player objects simply corresponds to the
327 sums of all statistical values.
328
329 Note that as soon as two players have been added, the 'home' property
330 becomes undefined.
331 """
332 assert self.playerid == player.playerid
333 new_player = Player(self.playerid, self.name, None)
334 stats1 = self.__stats
335 stats2 = player.__stats
336
337
338 for category, stats in stats1.iteritems():
339 new_player._add_stats(category, stats)
340
341
342
343 for category, stats in stats2.iteritems():
344 if not new_player.__dict__[category]:
345 assert category not in new_player.__stats
346 new_player._add_stats(category, stats)
347 else:
348 assert category in new_player.__stats
349 for k, v in stats.iteritems():
350 new_player.__stats[category][k] += v
351
352 return new_player
353