1 import csv
2 import functools
3 import itertools
4 import operator
5
6 from nflgame import OrderedDict
7
8 _BUILTIN_PREDS = {
9 '__lt': operator.lt,
10 '__le': operator.le,
11 '__ne': operator.ne,
12 '__ge': operator.ge,
13 '__gt': operator.gt,
14 }
15 """
16 A dictionary of suffixes to predicates that can be used in Gen.filter.
17 The suffix corresponds to what to add to the end of a field name to invoke
18 the predicate it corresponds to. For example, this::
19
20 players.filter(receiving_rec=lambda v: v > 0)
21
22 Is equivalent to::
23
24 players.filter(receiving_rec__gt=0)
25
26 (Django users should feel right at home.)
27 """
28
29
31 """
32 Players implements a sequence type and provides a convenient API for
33 searching sets of players.
34 """
35
37 """
38 Creates a new Players sequence from an iterable where each element
39 of the iterable is an instance of the Player class.
40 """
41 self.__iter = iterable
42
44 """
45 filters the sequence based on a set of criteria. Parameter
46 names should be equivalent to the properties accessible in the items
47 of the sequence. For example, where the items are instances of
48 the Stats class::
49
50 players.filter(home=True, passing_tds=1, rushing_yds=lambda x: x>0)
51
52 Returns a sequence with only players on the home team that
53 have a single passing touchdown and more than zero rushing yards.
54
55 If a field specified does not exist for a particular item, that
56 item is excluded from the result set.
57
58 If a field is set to a value, then only items with fields that equal
59 that value are returned.
60
61 If a field is set to a function---which must be a predicate---then
62 only items with field values satisfying that function will
63 be returned.
64
65 Also, special suffixes that begin with '__' may be added to the
66 end of a field name to invoke built in predicates.
67 For example, this::
68
69 players.filter(receiving_rec=lambda v: v > 0)
70
71 Is equivalent to::
72
73 players.filter(receiving_rec__gt=0)
74
75 Other suffixes includes gt, le, lt, ne, ge, etc.
76
77 (Django users should feel right at home.)
78 """
79 preds = []
80 for k, v in kwargs.iteritems():
81 def pred(field, value, item):
82 for suffix, p in _BUILTIN_PREDS.iteritems():
83 if field.endswith(suffix):
84 f = field[:field.index(suffix)]
85 if f not in item.__dict__:
86 return False
87 return p(item.__dict__[f], value)
88 if field not in item.__dict__:
89 return False
90 if isinstance(value, type(lambda x: x)):
91 return value(item.__dict__[field])
92 return item.__dict__[field] == value
93 preds.append(functools.partial(pred, k, v))
94
95 gen = itertools.ifilter(lambda item: all([f(item) for f in preds]),
96 self)
97 return self.__class__(gen)
98
100 """
101 Limit the sequence to N items.
102 """
103 return self.__class__(itertools.islice(self, n))
104
105 - def sort(self, field, descending=True):
106 """
107 sorts the sequence according to the field specified---where field is
108 a property on an item in the sequence. If descending is false, items
109 will be sorted in order from least to greatest.
110
111 Note that if field does not exist in any item being sorted, a
112 KeyError will be raised.
113 """
114 return self.__class__(sorted(self, reverse=descending,
115 key=lambda item: item.__dict__[field]))
116
118 """Returns a list of items in the sequence."""
119 return '[%s]' % ', '.join([str(item) for item in self])
120
122 """Make this an iterable sequence."""
123 if self.__iter is None:
124 return iter([])
125 if isinstance(self.__iter, OrderedDict):
126 return self.__iter.itervalues()
127 return iter(self.__iter)
128
130 """Satisfy the built in reversed."""
131 return reversed(self.__iter)
132
133
135 """
136 GenDrives implements a sequence type and provides a convenient API
137 for searching drives.
138 """
140 """
141 Returns all of the plays, in order, belonging to every drive in
142 the sequence.
143 """
144 return GenPlays(itertools.chain(*map(lambda d: d.plays, self)))
145
147 """
148 Returns the combined player stats for every player that participated
149 in any of the drives in the sequence.
150 """
151 return self.plays().players()
152
153 - def number(self, n, team=None):
154 """
155 Gets the Nth drive where the first drive corresponds to n=1. This is
156 only useful given a complete collection of drives for an entire game.
157
158 If the team parameter is specified (i.e., team='NE'), then n will
159 be interpreted as *that* team's Nth drive.
160 """
161 assert n > 0
162 n -= 1
163 if team is None:
164 return list(self)[n]
165 else:
166 i = 0
167 for d in self:
168 if d.team == team:
169 if i == n:
170 return d
171 i += 1
172 assert False, \
173 'Could not find drive %d for team %s.' % (n + 1, team)
174
175
177 """
178 GenPlays implements a sequence type and provides a convenient API
179 for searching plays.
180 """
193
194
196 """
197 GenPlayerStats implements a sequence type and provides a convenient API for
198 searching sets of player statistics.
199 """
200 - def name(self, name):
201 """
202 Returns a single player whose name equals `name`. If no such player
203 can be found, None is returned.
204
205 Note that NFL GameCenter formats their names like "T.Brady" and
206 "W.Welker". Thus, `name` should also be in this format.
207 """
208 for p in self:
209 if p.name == name:
210 return p
211 return None
212
214 """
215 Returns a single player whose NFL GameCenter identifier equals
216 `playerid`. This probably isn't too useful, unless you're trying
217 to do ID mapping. (Players have different identifiers across NFL.com.)
218
219 If no such player with the given identifier is found, None is
220 returned.
221 """
222 for p in self:
223 if p.playerid == playerid:
224 return p
225 return None
226
228 """
229 touchdowns is a convenience method for returning a Players
230 sequence of all players with at least one touchdown.
231 """
232 def gen():
233 for p in self:
234 for f in p.__dict__:
235 if f.endswith('tds') and p.__dict__[f] > 0:
236 yield p
237 break
238 return self.__class__(gen())
239
241 return self.__class__(itertools.ifilter(lambda p: p.has_cat(cat),
242 self))
243
245 """Returns players that have a "passing" statistical category."""
246 return self.__filter_category('passing')
247
249 """Returns players that have a "rushing" statistical category."""
250 return self.__filter_category('rushing')
251
253 """Returns players that have a "receiving" statistical category."""
254 return self.__filter_category('receiving')
255
257 """Returns players that have a "fumbles" statistical category."""
258 return self.__filter_category('fumbles')
259
261 """Returns players that have a "kicking" statistical category."""
262 return self.__filter_category('kicking')
263
265 """Returns players that have a "punting" statistical category."""
266 return self.__filter_category('punting')
267
269 """Returns players that have a "kickret" statistical category."""
270 return self.__filter_category('kickret')
271
273 """Returns players that have a "puntret" statistical category."""
274 return self.__filter_category('puntret')
275
277 """Returns players that have a "defense" statistical category."""
278 return self.__filter_category('defense')
279
281 """Returns players that have a "penalty" statistical category."""
282 return self.__filter_category('penalty')
283
284 - def csv(self, fileName):
285 """
286 Given a file-name fileName, csv will write the contents of
287 the Players sequence to fileName formatted as comma-separated values.
288 The resulting file can then be opened directly with programs like
289 Excel, Google Docs, Libre Office and Open Office.
290
291 Note that since each player in a Players sequence may have differing
292 statistical categories (like a quarterback and a receiver), the
293 minimum constraining set of statisical categories is used as the
294 header row for the resulting CSV file.
295 """
296 fields, rows = [], []
297 players = list(self)
298 for p in players:
299 for category, stats in p.all_stats().iteritems():
300 for stat in stats:
301 field = '%s_%s' % (category, stat)
302 if field in fields:
303 continue
304 fields.append(field)
305 for p in players:
306 d = {
307 'name': p.name,
308 'id': p.playerid,
309 'home': p.home and 'yes' or 'no',
310 }
311 for field in fields:
312 if field in p.__dict__:
313 d[field] = p.__dict__[field]
314 else:
315 d[field] = ""
316 rows.append(d)
317
318 fieldNames = ["name", "id", "home"] + fields
319 rows = [dict((f, f) for f in fieldNames)] + rows
320 csv.DictWriter(open(fileName, 'w+'), fieldNames).writerows(rows)
321
334