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