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