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