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