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 not hasattr(item, f) or getattr(item, f) is None:
86 return False
87 return p(getattr(item, f), value)
88 if not hasattr(item, field) or getattr(item, field) is None:
89 return False
90 if isinstance(value, type(lambda x: x)):
91 return value(getattr(item, field))
92 return getattr(item, 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 def attrget(item):
115 return getattr(item, field, 0)
116
117 return self.__class__(sorted(self, reverse=descending, key=attrget))
118
120 """Returns a list of items in the sequence."""
121 return '[%s]' % ', '.join([str(item) for item in self])
122
124 """Make this an iterable sequence."""
125 if self.__iter is None:
126 return iter([])
127 if isinstance(self.__iter, OrderedDict):
128 return self.__iter.itervalues()
129 return iter(self.__iter)
130
132 """Satisfy the built in reversed."""
133 return reversed(self.__iter)
134
135
137 """
138 GenDrives implements a sequence type and provides a convenient API
139 for searching drives.
140 """
142 """
143 Returns all of the plays, in order, belonging to every drive in
144 the sequence.
145 """
146 return GenPlays(itertools.chain(*map(lambda d: d.plays, self)))
147
149 """
150 Returns the combined player stats for every player that participated
151 in any of the drives in the sequence.
152 """
153 return self.plays().players()
154
155 - def number(self, n, team=None):
156 """
157 Gets the Nth drive where the first drive corresponds to n=1. This is
158 only useful given a complete collection of drives for an entire game.
159
160 If the team parameter is specified (i.e., team='NE'), then n will
161 be interpreted as *that* team's Nth drive.
162 """
163 assert n > 0
164 n -= 1
165 if team is None:
166 return list(self)[n]
167 else:
168 i = 0
169 for d in self:
170 if d.team == team:
171 if i == n:
172 return d
173 i += 1
174 assert False, \
175 'Could not find drive %d for team %s.' % (n + 1, team)
176
177
179 """
180 GenPlays implements a sequence type and provides a convenient API
181 for searching plays.
182 """
195
196
198 """
199 GenPlayerStats implements a sequence type and provides a convenient API for
200 searching sets of player statistics.
201 """
202 - def name(self, name):
203 """
204 Returns a single player whose name equals `name`. If no such player
205 can be found, None is returned.
206
207 Note that NFL GameCenter formats their names like "T.Brady" and
208 "W.Welker". Thus, `name` should also be in this format.
209 """
210 for p in self:
211 if p.name == name:
212 return p
213 return None
214
216 """
217 Returns a single player whose NFL GameCenter identifier equals
218 `playerid`. This probably isn't too useful, unless you're trying
219 to do ID mapping. (Players have different identifiers across NFL.com.)
220
221 If no such player with the given identifier is found, None is
222 returned.
223 """
224 for p in self:
225 if p.playerid == playerid:
226 return p
227 return None
228
230 """
231 touchdowns is a convenience method for returning a Players
232 sequence of all players with at least one touchdown.
233 """
234 def gen():
235 for p in self:
236 for f in p.__dict__:
237 if f.endswith('tds') and p.__dict__[f] > 0:
238 yield p
239 break
240 return self.__class__(gen())
241
243 return self.__class__(itertools.ifilter(lambda p: p.has_cat(cat),
244 self))
245
247 """Returns players that have a "passing" statistical category."""
248 return self.__filter_category('passing')
249
251 """Returns players that have a "rushing" statistical category."""
252 return self.__filter_category('rushing')
253
255 """Returns players that have a "receiving" statistical category."""
256 return self.__filter_category('receiving')
257
259 """Returns players that have a "fumbles" statistical category."""
260 return self.__filter_category('fumbles')
261
263 """Returns players that have a "kicking" statistical category."""
264 return self.__filter_category('kicking')
265
267 """Returns players that have a "punting" statistical category."""
268 return self.__filter_category('punting')
269
271 """Returns players that have a "kickret" statistical category."""
272 return self.__filter_category('kickret')
273
275 """Returns players that have a "puntret" statistical category."""
276 return self.__filter_category('puntret')
277
279 """Returns players that have a "defense" statistical category."""
280 return self.__filter_category('defense')
281
283 """Returns players that have a "penalty" statistical category."""
284 return self.__filter_category('penalty')
285
286 - def csv(self, fileName):
287 """
288 Given a file-name fileName, csv will write the contents of
289 the Players sequence to fileName formatted as comma-separated values.
290 The resulting file can then be opened directly with programs like
291 Excel, Google Docs, Libre Office and Open Office.
292
293 Note that since each player in a Players sequence may have differing
294 statistical categories (like a quarterback and a receiver), the
295 minimum constraining set of statisical categories is used as the
296 header row for the resulting CSV file.
297 """
298 fields, rows = [], []
299 players = list(self)
300 for p in players:
301 for field, stat in p.stats.iteritems():
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