correspond to the `UNK` (unknown) team.
"""
self.position = None
"""
The current position of a player if it's available. This may
be **not** be `None`. If the position is not known, then the
`UNK` enum is used from `nfldb.Enums.player_pos`.
"""
self.profile_id = None
"""
The profile identifier used on a player's canonical NFL.com
profile page. This is used as a foreign key to connect varying
sources of information.
"""
self.profile_url = None
"""The NFL.com profile URL for this player."""
self.uniform_number = None
"""A player's uniform number as an integer."""
self.birthdate = None
"""A player's birth date as a free-form string."""
self.college = None
"""A player's college as a free-form string."""
self.height = None
"""A player's height as a free-form string."""
self.weight = None
"""A player's weight as a free-form string."""
self.years_pro = None
"""The number of years a player has played as an integer."""
self.status = None
"""The current status of this player as a free-form string."""
def from_row_dict(
cls, db, row)
Introduces a new entity object from a full SQL row result from
the entity's tables. (i.e., row
is a dictionary mapping
column to value.) Note that the column names must be of the
form '{entity_name}_{column_name}'. For example, in the game
table, the gsis_id
column must be named game_gsis_id
in
row
.
@classmethod
def from_row_dict(cls, db, row):
"""
Introduces a new entity object from a full SQL row result from
the entity's tables. (i.e., `row` is a dictionary mapping
column to value.) Note that the column names must be of the
form '{entity_name}_{column_name}'. For example, in the `game`
table, the `gsis_id` column must be named `game_gsis_id` in
`row`.
"""
obj = cls(db)
seta = setattr
prefix = cls._sql_primary_table() + '_'
slice_from = len(prefix)
for k in row:
if k.startswith(prefix):
seta(obj, k[slice_from:], row[k])
return obj
def from_row_tuple(
cls, db, t)
Given a tuple t
corresponding to a result from a SELECT query,
this will construct a new instance for this entity. Note that
the tuple t
must be in exact correspondence with the columns
returned by nfldb.Entity.sql_fields
.
@classmethod
def from_row_tuple(cls, db, t):
"""
Given a tuple `t` corresponding to a result from a SELECT query,
this will construct a new instance for this entity. Note that
the tuple `t` must be in *exact* correspondence with the columns
returned by `nfldb.Entity.sql_fields`.
"""
cols = cls.sql_fields()
seta = setattr
obj = cls(db)
for i, field in enumerate(cols):
seta(obj, field, t[i])
return obj
def sql_fields(
cls)
Returns a list of all SQL fields across all tables for this
entity, including derived fields. This method can be used
in conjunction with nfldb.Entity.from_row_tuple
to quickly
create new nfldb
objects without every constructing a dict.
@classmethod
def sql_fields(cls):
"""
Returns a list of all SQL fields across all tables for this
entity, including derived fields. This method can be used
in conjunction with `nfldb.Entity.from_row_tuple` to quickly
create new `nfldb` objects without every constructing a dict.
"""
if not hasattr(cls, '_cached_sql_fields'):
cls._cached_sql_fields = cls._sql_columns()
cls._cached_sql_fields += cls._sql_tables['derived']
return cls._cached_sql_fields
class PossessionTime
Represents the possession time of a drive in seconds.
This class defines a total ordering on possession times. Namely, p1 < p2 if and only if p2 corresponds to a longer time of possession than p1.
class PossessionTime (object):
"""
Represents the possession time of a drive in seconds.
This class defines a total ordering on possession times. Namely, p1
< p2 if and only if p2 corresponds to a longer time of possession
than p1.
"""
__slots__ = ['_seconds']
@staticmethod
def from_str(clock_str):
"""
Introduces a `nfldb.PossessionTime` object from a string
formatted as clock time. For example, `2:00` corresponds to
`120` seconds and `14:39` corresponds to `879` seconds.
"""
minutes, seconds = map(int, clock_str.split(':', 1))
return PossessionTime((minutes * 60) + seconds)
@staticmethod
def _pg_cast(sqlv, cursor):
return PossessionTime(int(sqlv[1:-1]))
def __init__(self, seconds):
"""
Returns a `nfldb.PossessionTime` object given the number of
seconds of the possession.
"""
assert isinstance(seconds, int)
self._seconds = seconds
@property
def valid(self):
"""
Returns `True` if and only if this possession time has a valid
representation.
Invalid possession times cannot be compared with other
possession times.
"""
return self._seconds is not None
@property
def total_seconds(self):
"""
The total seconds elapsed for this possession.
`0` is returned if this is not a valid possession time.
"""
return self._seconds if self.valid else 0
@property
def minutes(self):
"""
The number of whole minutes for a possession.
e.g., `0:59` would be `0` minutes and `4:01` would be `4`
minutes.
`0` is returned if this is not a valid possession time.
"""
return (self._seconds // 60) if self.valid else 0
@property
def seconds(self):
"""
The seconds portion of the possession time.
e.g., `0:59` would be `59` seconds and `4:01` would be `1`
second.
`0` is returned if this is not a valid possession time.
"""
return (self._seconds % 60) if self.valid else 0
def __str__(self):
if not self.valid:
return 'N/A'
else:
return '%02d:%02d' % (self.minutes, self.seconds)
def __lt__(self, other):
if self.__class__ is not other.__class__:
return NotImplemented
assert self.valid and other.valid
return self._seconds < other._seconds
def __eq__(self, other):
if self.__class__ is not other.__class__:
return NotImplemented
return self._seconds == other._seconds
def __conform__(self, proto):
if proto is ISQLQuote:
if not self.valid:
return AsIs("NULL")
else:
return AsIs("ROW(%d)::pos_period" % self._seconds)
return None
Ancestors (in MRO)
- PossessionTime
- __builtin__.object
Static methods
def from_str(
clock_str)
Introduces a PossessionTime
object from a string
formatted as clock time. For example, 2:00
corresponds to
120
seconds and 14:39
corresponds to 879
seconds.
@staticmethod
def from_str(clock_str):
"""
Introduces a `nfldb.PossessionTime` object from a string
formatted as clock time. For example, `2:00` corresponds to
`120` seconds and `14:39` corresponds to `879` seconds.
"""
minutes, seconds = map(int, clock_str.split(':', 1))
return PossessionTime((minutes * 60) + seconds)
Instance variables
var minutes
The number of whole minutes for a possession.
e.g., 0:59
would be 0
minutes and 4:01
would be 4
minutes.
0
is returned if this is not a valid possession time.
var seconds
The seconds portion of the possession time.
e.g., 0:59
would be 59
seconds and 4:01
would be 1
second.
0
is returned if this is not a valid possession time.
var total_seconds
The total seconds elapsed for this possession.
0
is returned if this is not a valid possession time.
var valid
Returns True
if and only if this possession time has a valid
representation.
Invalid possession times cannot be compared with other possession times.
Methods
def __init__(
self, seconds)
Returns a PossessionTime
object given the number of
seconds of the possession.
def __init__(self, seconds):
"""
Returns a `nfldb.PossessionTime` object given the number of
seconds of the possession.
"""
assert isinstance(seconds, int)
self._seconds = seconds
class Query
A query represents a set of criteria to search nfldb's PostgreSQL database. Its primary feature is to provide a high-level API for searching NFL game, drive, play and player data very quickly.
The basic workflow is to specify all of the search criteria that
you want, and then use one of the as_*
methods to actually
perform the search and return results from the database.
For example, to get all Patriots games as Game
objects from
the 2012 regular season, we could do:
#!python
q = Query(db).game(season_year=2012, season_type='Regular', team='NE')
for game in q.as_games():
print game
Other comparison operators like <
or >=
can also be used. To use
them, append a suffix like __lt
to the end of a field name. So to get
all games with a home score greater than or equal to 50:
#!python
q = Query(db).game(home_score__ge=50)
for game in q.as_games():
print game
Other suffixes are available: __lt
for <
, __le
for <=
,
__gt
for >
, __ge
for >=
, __ne
for !=
and __eq
for
==
. Although, the __eq
suffix is used by default and is
therefore never necessary to use.
More criteria can be specified by chaining search criteria. For
example, to get only plays as Play
objects where Tom Brady
threw a touchdown pass:
#!python
q = Query(db).game(season_year=2012, season_type='Regular')
q.player(full_name="Tom Brady").play(passing_tds=1)
for play in q.as_plays():
print play
By default, all critera specified are combined conjunctively (i.e., all criteria must be met for each result returned). However, sometimes you may want to specify disjunctive criteria (i.e., any of the criteria can be met for a result to be returned). To do this for a single field, simply use a list. For example, to get all Patriot games from the 2009 to 2013 seasons:
#!python
q = Query(db).game(season_type='Regular', team='NE')
q.game(season_year=[2009, 2010, 2011, 2012, 2013])
for game in q.as_games():
print game
Disjunctions can also be applied to multiple fields by creating a
Query
object with QueryOR
. For example, to find
all games where either team had more than 50 points:
#!python
q = QueryOR(db).game(home_score__ge=50, away_score__ge=50)
for game in q.as_games():
print game
Finally, multiple queries can be combined with andalso
.
For example, to restrict the last search to games in the 2012 regular
season:
#!python
big_score = QueryOR(db).game(home_score__ge=50, away_score__ge=50)
q = Query(db).game(season_year=2012, season_type='Regular')
q.andalso(big_score)
for game in q.as_games():
print game
This is only the beginning of what can be done. More examples that run the gamut can be found on nfldb's wiki.
class Query (Condition):
"""
A query represents a set of criteria to search nfldb's PostgreSQL
database. Its primary feature is to provide a high-level API for
searching NFL game, drive, play and player data very quickly.
The basic workflow is to specify all of the search criteria that
you want, and then use one of the `as_*` methods to actually
perform the search and return results from the database.
For example, to get all Patriots games as `nfldb.Game` objects from
the 2012 regular season, we could do:
#!python
q = Query(db).game(season_year=2012, season_type='Regular', team='NE')
for game in q.as_games():
print game
Other comparison operators like `<` or `>=` can also be used. To use
them, append a suffix like `__lt` to the end of a field name. So to get
all games with a home score greater than or equal to 50:
#!python
q = Query(db).game(home_score__ge=50)
for game in q.as_games():
print game
Other suffixes are available: `__lt` for `<`, `__le` for `<=`,
`__gt` for `>`, `__ge` for `>=`, `__ne` for `!=` and `__eq` for
`==`. Although, the `__eq` suffix is used by default and is
therefore never necessary to use.
More criteria can be specified by chaining search criteria. For
example, to get only plays as `nfldb.Play` objects where Tom Brady
threw a touchdown pass:
#!python
q = Query(db).game(season_year=2012, season_type='Regular')
q.player(full_name="Tom Brady").play(passing_tds=1)
for play in q.as_plays():
print play
By default, all critera specified are combined conjunctively (i.e.,
all criteria must be met for each result returned). However,
sometimes you may want to specify disjunctive criteria (i.e., any
of the criteria can be met for a result to be returned). To do this
for a single field, simply use a list. For example, to get all
Patriot games from the 2009 to 2013 seasons:
#!python
q = Query(db).game(season_type='Regular', team='NE')
q.game(season_year=[2009, 2010, 2011, 2012, 2013])
for game in q.as_games():
print game
Disjunctions can also be applied to multiple fields by creating a
`nfldb.Query` object with `nfldb.QueryOR`. For example, to find
all games where either team had more than 50 points:
#!python
q = QueryOR(db).game(home_score__ge=50, away_score__ge=50)
for game in q.as_games():
print game
Finally, multiple queries can be combined with `nfldb.Query.andalso`.
For example, to restrict the last search to games in the 2012 regular
season:
#!python
big_score = QueryOR(db).game(home_score__ge=50, away_score__ge=50)
q = Query(db).game(season_year=2012, season_type='Regular')
q.andalso(big_score)
for game in q.as_games():
print game
This is only the beginning of what can be done. More examples that run
the gamut can be found on
[nfldb's wiki](https://github.com/BurntSushi/nfldb/wiki).
"""
def __init__(self, db, orelse=False):
"""
Introduces a new `nfldb.Query` object. Criteria can be
added with any combination of the `nfldb.Query.game`,
`nfldb.Query.drive`, `nfldb.Query.play`, `nfldb.Query.player`
and `nfldb.Query.aggregate` methods. Results can
then be retrieved with any of the `as_*` methods:
`nfldb.Query.as_games`, `nfldb.Query.as_drives`,
`nfldb.Query.as_plays`, `nfldb.Query.as_play_players`,
`nfldb.Query.as_players` and `nfldb.Query.as_aggregate`.
Note that if aggregate criteria are specified with
`nfldb.Query.aggregate`, then the **only** way to retrieve
results is with the `nfldb.Query.as_aggregate` method. Invoking
any of the other `as_*` methods will raise an assertion error.
"""
self._db = db
"""A psycopg2 database connection object."""
self._sort_exprs = None
"""Expressions used to sort the results."""
self._limit = None
"""The number of results to limit the search to."""
self._andalso = []
"""A list of conjunctive conditions."""
self._orelse = []
"""
A list of disjunctive conditions applied to
`Query._andalso`.
"""
self._default_cond = self._orelse if orelse else self._andalso
"""
Whether to use conjunctive or disjunctive conditions by
default.
"""
# The aggregate counter-parts of the above.
self._agg_andalso, self._agg_orelse = [], []
if orelse:
self._agg_default_cond = self._agg_orelse
else:
self._agg_default_cond = self._agg_andalso
def sort(self, exprs):
"""
Specify sorting criteria for the result set returned by
using sort expressions. A sort expression is a tuple with
two elements: a field to sort by and the order to use. The
field should correspond to an attribute of the objects you're
returning and the order should be `asc` for ascending (smallest
to biggest) or `desc` for descending (biggest to smallest).
For example, `('passing_yds', 'desc')` would sort plays by the
number of passing yards in the play, with the biggest coming
first.
Remember that a sort field must be an attribute of the
results being returned. For example, you can't sort plays by
`home_score`, which is an attribute of a `nfldb.Game` object.
If you require this behavior, you will need to do it in Python
with its `sorted` built in function. (Or alternatively, use
two separate queries if the result set is large.)
You may provide multiple sort expressions. For example,
`[('gsis_id', 'asc'), ('time', 'asc'), ('play_id', 'asc')]`
would sort plays in the order in which they occurred within
each game.
`exprs` may also just be a string specifying a single
field which defaults to a descending order. For example,
`sort('passing_yds')` sorts plays by passing yards in
descending order.
If `exprs` is set to the empty list, then sorting will be
disabled for this query.
Note that sorting criteria can be combined with
`nfldb.Query.limit` to limit results which can dramatically
speed up larger searches. For example, to fetch the top 10
passing plays in the 2012 season:
#!python
q = Query(db).game(season_year=2012, season_type='Regular')
q.sort('passing_yds').limit(10)
for p in q.as_plays():
print p
A more naive approach might be to fetch all plays and sort them
with Python:
#!python
q = Query(db).game(season_year=2012, season_type='Regular')
plays = q.as_plays()
plays = sorted(plays, key=lambda p: p.passing_yds, reverse=True)
for p in plays[:10]:
print p
But this is over **43 times slower** on my machine than using
`nfldb.Query.sort` and `nfldb.Query.limit`. (The performance
difference is due to making PostgreSQL perform the search and
restricting the number of results returned to process.)
"""
self._sort_exprs = exprs
return self
def limit(self, count):
"""
Limits the number of results to the integer `count`. If `count` is
`0` (the default), then no limiting is done.
See the documentation for `nfldb.Query.sort` for an example on how
to combine it with `nfldb.Query.limit` to get results quickly.
"""
self._limit = count
return self
def _sorter(self, default_entity):
return Sorter(default_entity, self._sort_exprs, self._limit)
def _assert_no_aggregate(self):
assert len(self._agg_andalso) == 0 and len(self._agg_orelse) == 0, \
'aggregate criteria are only compatible with as_aggregate'
def andalso(self, *conds):
"""
Adds the list of `nfldb.Query` objects in `conds` to this
query's list of conjunctive conditions.
"""
self._andalso += conds
return self
def orelse(self, *conds):
"""
Adds the list of `nfldb.Query` objects in `conds` to this
query's list of disjunctive conditions.
"""
self._orelse += conds
return self
def game(self, **kw):
"""
Specify search criteria for an NFL game. The possible fields
correspond to columns in the `game` table (or derived columns).
They are documented as instance variables in the `nfldb.Game`
class. Additionally, there are some special fields that provide
convenient access to common conditions:
* **team** - Find games that the team given played in, regardless
of whether it is the home or away team.
Please see the documentation for `nfldb.Query` for examples on
how to specify search criteria.
Please
[open an issue](https://github.com/BurntSushi/nfldb/issues/new)
if you can think of other special fields to add.
"""
if 'team' in kw:
team = kw.pop('team')
ors = {'home_team': team, 'away_team': team}
self.andalso(Query(self._db, orelse=True).game(**ors))
_append_conds(self._default_cond, types.Game, kw)
return self
def drive(self, **kw):
"""
Specify search criteria for a drive. The possible fields
correspond to columns in the `drive` table (or derived
columns). They are documented as instance variables in the
`nfldb.Drive` class.
Please see the documentation for `nfldb.Query` for examples on
how to specify search criteria.
"""
_append_conds(self._default_cond, types.Drive, kw)
return self
def play(self, **kw):
"""
Specify search criteria for a play. The possible fields
correspond to columns in the `play` or `play_player` tables (or
derived columns). They are documented as instance variables in
the `nfldb.Play` and `nfldb.PlayPlayer` classes. Additionally,
the fields listed on the
[statistical categories](http://goo.gl/1qYG3C)
wiki page may be used. That includes **both** `play` and
`player` statistical categories.
Please see the documentation for `nfldb.Query` for examples on
how to specify search criteria.
"""
_append_conds(self._default_cond, types.Play, kw)
return self
def play_player(self, **kw):
"""
Specify search criteria for individual play player statistics.
The allowed fields are the columns in the `play_player`
table. They are documented as instance variables in the
`nfldb.PlayPlayer` class. Additionally, the fields listed on
the [statistical categories](http://goo.gl/1qYG3C) wiki page
may be used. (Only the `player` statistical categories.)
This method differs from `nfldb.Query.play` in that it can be
used to select for individual player statistics in a play. In
particular, there are *zero or more* player statistics for
every play.
"""
# Technically, it isn't necessary to handle derived fields manually
# since their SQL can be generated automatically, but it can be
# much faster to express them in terms of boolean logic with other
# fields rather than generate them.
for field, value in kw.items():
nosuff = _no_comp_suffix(field)
suff = _comp_suffix(field)
def replace_or(*fields):
q = Query(self._db, orelse=True)
ors = dict([('%s__%s' % (f, suff), value) for f in fields])
self.andalso(q.play_player(**ors))
if nosuff in types.PlayPlayer._derived_combined:
replace_or(*types.PlayPlayer._derived_combined[nosuff])
kw.pop(field)
# Now add the rest of the query.
_append_conds(self._default_cond, types.PlayPlayer, kw)
return self
def player(self, **kw):
"""
Specify search criteria for a player. The possible fields
correspond to columns in the `player` table (or derived
columns). They are documented as instance variables in the
`nfldb.Player` class.
Please see the documentation for `nfldb.Query` for examples on
how to specify search criteria.
"""
_append_conds(self._default_cond, types.Player, kw)
return self
def aggregate(self, **kw):
"""
This is just like `nfldb.Query.play_player`, except the search
parameters are applied to aggregate statistics.
For example, to retrieve all quarterbacks who passed for at
least 4000 yards in the 2012 season:
#!python
q = Query(db).game(season_year=2012, season_type='Regular')
q.aggregate(passing_yds__ge=4000)
for pp in q.as_aggregate():
print pp.player, pp.passing_yds
Aggregate results can also be sorted:
#!python
for pp in q.sort('passing_yds').as_aggregate():
print pp.player, pp.passing_yds
Note that this method can **only** be used with
`nfldb.Query.as_aggregate`. Use with any of the other
`as_*` methods will result in an assertion error. Note
though that regular criteria can still be specified with
`nfldb.Query.game`, `nfldb.Query.play`, etc. (Regular criteria
restrict *what to aggregate* while aggregate criteria restrict
*aggregated results*.)
"""
_append_conds(self._agg_default_cond, types.PlayPlayer, kw)
return self
def _make_join_query(self, cursor, entity, only_prim=False, sorter=None,
ent_fillers=None):
if sorter is None:
sorter = self._sorter(entity)
entities = self._entities()
entities.update(sorter.entities)
for ent in ent_fillers or []:
entities.add(ent)
entities.discard(entity)
# If we're joining the `player` table with any other table except
# `play_player`, then we MUST add `play_player` as a joining table.
# It is the only way to bridge players and games/drives/plays.
#
# TODO: This could probably be automatically deduced in general case,
# but we only have one case so just check for it manually.
if (entity is not types.PlayPlayer and types.Player in entities) \
or (entity is types.Player and len(entities) > 0):
entities.add(types.PlayPlayer)
if only_prim:
columns = entity._sql_tables['primary']
fields = entity._sql_select_fields(fields=columns)
else:
fields = []
for ent in ent_fillers or []:
fields += ent._sql_select_fields(fields=ent.sql_fields())
fields += entity._sql_select_fields(fields=entity.sql_fields())
args = {
'columns': ', '.join(fields),
'from': entity._sql_from(),
'joins': entity._sql_join_all(entities),
'where': sql.ands(self._sql_where(cursor)),
'groupby': '',
'sortby': sorter.sql(),
}
# We need a GROUP BY if we're joining with a table that has more
# specific information. e.g., selecting from game with criteria
# for plays.
if any(entity._sql_relation_distance(to) > 0 for to in entities):
fields = []
for table, _ in entity._sql_tables['tables']:
fields += entity._sql_primary_key(table)
args['groupby'] = 'GROUP BY ' + ', '.join(fields)
q = '''
SELECT {columns} {from} {joins}
WHERE {where}
{groupby}
{sortby}
'''.format(**args)
return q
def as_games(self):
"""
Executes the query and returns the results as a list of
`nfldb.Game` objects.
"""
self._assert_no_aggregate()
results = []
with Tx(self._db, factory=tuple_cursor) as cursor:
q = self._make_join_query(cursor, types.Game)
cursor.execute(q)
for row in cursor.fetchall():
results.append(types.Game.from_row_tuple(self._db, row))
return results
def as_drives(self):
"""
Executes the query and returns the results as a list of
`nfldb.Drive` objects.
"""
self._assert_no_aggregate()
results = []
with Tx(self._db, factory=tuple_cursor) as cursor:
q = self._make_join_query(cursor, types.Drive)
cursor.execute(q)
for row in cursor.fetchall():
results.append(types.Drive.from_row_tuple(self._db, row))
return results
def as_plays(self, fill=True):
"""
Executes the query and returns the results as a dictionary
of `nlfdb.Play` objects that don't have the `play_player`
attribute filled. The keys of the dictionary are play id
tuples with the spec `(gsis_id, drive_id, play_id)`.
The primary key membership SQL expression is also returned.
"""
def make_pid(play):
return (play.gsis_id, play.drive_id, play.play_id)
self._assert_no_aggregate()
# This is pretty terrifying.
# Apparently PostgreSQL can change the order of rows returned
# depending on the columns selected. So e.g., if you sort by `down`
# and limit to 20 results, you might get a different 20 plays if
# you change which columns you're selecting.
# This is pertinent here because if we're filling plays with player
# statistics, then we are assuming that this order never changes.
# To make the ordering consistent, we add the play's primary key to
# the existing sort criteria, which guarantees that the sort will
# always be the same.
# (We are careful not to override the user specified
# `self._sort_exprs`.)
#
# That was a lie. We override the user settings if the user asks
# to sort by `gsis_id`, `drive_id` or `play_id`.
consistent = [(c, 'asc') for c in ['gsis_id', 'drive_id', 'play_id']]
sorter = Sorter(types.Play, self._sort_exprs, self._limit)
sorter.add_exprs(*consistent)
if not fill:
results = []
with Tx(self._db, factory=tuple_cursor) as cursor:
init = types.Play.from_row_tuple
q = self._make_join_query(cursor, types.Play, sorter=sorter)
cursor.execute(q)
for row in cursor.fetchall():
results.append(init(self._db, row))
return results
else:
plays = OrderedDict()
with Tx(self._db, factory=tuple_cursor) as cursor:
init_play = types.Play.from_row_tuple
q = self._make_join_query(cursor, types.Play, sorter=sorter)
cursor.execute(q)
for row in cursor.fetchall():
play = init_play(self._db, row)
play._play_players = []
plays[make_pid(play)] = play
# Run the above query *again* as a subquery.
# This time, only fetch the primary key, and use that to
# fetch all the `play_player` records in one swoop.
aliases = {'play_player': 'pp'}
ids = self._make_join_query(cursor, types.Play,
only_prim=True, sorter=sorter)
from_tables = types.PlayPlayer._sql_from(aliases=aliases)
columns = types.PlayPlayer._sql_select_fields(
fields=types.PlayPlayer.sql_fields(), aliases=aliases)
q = '''
SELECT {columns} {from_tables}
WHERE (pp.gsis_id, pp.drive_id, pp.play_id) IN ({ids})
'''.format(columns=', '.join(columns),
from_tables=from_tables, ids=ids)
init_pp = types.PlayPlayer.from_row_tuple
cursor.execute(q)
for row in cursor.fetchall():
pp = init_pp(self._db, row)
plays[make_pid(pp)]._play_players.append(pp)
return plays.values()
def as_play_players(self):
"""
Executes the query and returns the results as a list of
`nlfdb.PlayPlayer` objects.
This provides a way to access player statistics directly
by bypassing play data. Usually the results of this method
are passed to `nfldb.aggregate`. It is recommended to use
`nfldb.Query.aggregate` and `nfldb.Query.as_aggregate` when
possible, since it is significantly faster to sum statistics in
the database as opposed to Python.
"""
self._assert_no_aggregate()
results = []
with Tx(self._db, factory=tuple_cursor) as cursor:
init = types.PlayPlayer.from_row_tuple
q = self._make_join_query(cursor, types.PlayPlayer)
cursor.execute(q)
for row in cursor.fetchall():
results.append(init(self._db, row))
return results
def as_players(self):
"""
Executes the query and returns the results as a list of
`nfldb.Player` objects.
"""
self._assert_no_aggregate()
results = []
with Tx(self._db) as cursor:
q = self._make_join_query(cursor, types.Player)
cursor.execute(q)
for row in cursor.fetchall():
results.append(types.Player.from_row_dict(self._db, row))
return results
def as_aggregate(self):
"""
Executes the query and returns the results as aggregated
`nfldb.PlayPlayer` objects. This method is meant to be a more
restricted but much faster version of `nfldb.aggregate`.
Namely, this method uses PostgreSQL to compute the aggregate
statistics while `nfldb.aggregate` computes them in Python
code.
If any sorting criteria is specified, it is applied to the
aggregate *player* values only.
"""
class AggPP (types.PlayPlayer):
@classmethod
def _sql_field(cls, name, aliases=None):
if name in cls._derived_combined:
fields = cls._derived_combined[name]
fields = [cls._sql_field(f, aliases=aliases) for f in fields]
return ' + '.join(fields)
elif name == 'points':
fields = ['(%s * %d)' % (cls._sql_field(f, aliases=aliases), pval)
for f, pval in cls._point_values]
return ' + '.join(fields)
else:
sql = super(AggPP, cls)._sql_field(name, aliases=aliases)
return 'SUM(%s)' % sql
joins = ''
results = []
with Tx(self._db) as cur:
for ent in self._entities():
if ent is types.PlayPlayer:
continue
joins += types.PlayPlayer._sql_join_to_all(ent)
sum_fields = types._player_categories.keys() \
+ AggPP._sql_tables['derived']
select_sum_fields = AggPP._sql_select_fields(sum_fields)
where = self._sql_where(cur)
having = self._sql_where(cur, aggregate=True)
q = '''
SELECT
play_player.player_id AS play_player_player_id, {sum_fields}
FROM play_player
{joins}
WHERE {where}
GROUP BY play_player.player_id
HAVING {having}
{order}
'''.format(
sum_fields=', '.join(select_sum_fields),
joins=joins,
where=sql.ands(where),
having=sql.ands(having),
order=self._sorter(AggPP).sql(),
)
init = AggPP.from_row_dict
cur.execute(q)
for row in cur.fetchall():
results.append(init(self._db, row))
return results
def _entities(self):
"""
Returns all the entity types referenced in the search criteria.
"""
tabs = set()
for cond in self._andalso + self._orelse:
tabs = tabs.union(cond._entities())
return tabs
def show_where(self, aggregate=False):
"""
Returns an approximate WHERE clause corresponding to the
criteria specified in `self`. Note that the WHERE clause given
is never explicitly used for performance reasons, but one hopes
that it describes the criteria in `self`.
If `aggregate` is `True`, then aggregate criteria for the
`play` and `play_player` tables is shown with aggregate
functions applied.
"""
with Tx(self._db) as cur:
return self._sql_where(cur, aggregate=aggregate)
return ''
def _sql_where(self, cursor, aliases=None, aggregate=False):
"""
Returns a WHERE expression representing the search criteria
in `self` and restricted to the tables in `tables`.
If `aggregate` is `True`, then the appropriate aggregate
functions are used.
"""
if aggregate:
return Condition._disjunctions(
cursor, [self._agg_andalso] + [[c] for c in self._agg_orelse],
aliases=aliases, aggregate=aggregate)
else:
return Condition._disjunctions(
cursor, [self._andalso] + [[c] for c in self._orelse],
aliases=aliases, aggregate=aggregate)
Ancestors (in MRO)
- Query
- nfldb.query.Condition
- __builtin__.object
Methods
def __init__(
self, db, orelse=False)
Introduces a new Query
object. Criteria can be
added with any combination of the game
,
drive
, play
, player
and aggregate
methods. Results can
then be retrieved with any of the as_*
methods:
as_games
, as_drives
,
as_plays
, as_play_players
,
as_players
and as_aggregate
.
Note that if aggregate criteria are specified with
aggregate
, then the only way to retrieve
results is with the as_aggregate
method. Invoking
any of the other as_*
methods will raise an assertion error.
def __init__(self, db, orelse=False):
"""
Introduces a new `nfldb.Query` object. Criteria can be
added with any combination of the `nfldb.Query.game`,
`nfldb.Query.drive`, `nfldb.Query.play`, `nfldb.Query.player`
and `nfldb.Query.aggregate` methods. Results can
then be retrieved with any of the `as_*` methods:
`nfldb.Query.as_games`, `nfldb.Query.as_drives`,
`nfldb.Query.as_plays`, `nfldb.Query.as_play_players`,
`nfldb.Query.as_players` and `nfldb.Query.as_aggregate`.
Note that if aggregate criteria are specified with
`nfldb.Query.aggregate`, then the **only** way to retrieve
results is with the `nfldb.Query.as_aggregate` method. Invoking
any of the other `as_*` methods will raise an assertion error.
"""
self._db = db
"""A psycopg2 database connection object."""
self._sort_exprs = None
"""Expressions used to sort the results."""
self._limit = None
"""The number of results to limit the search to."""
self._andalso = []
"""A list of conjunctive conditions."""
self._orelse = []
"""
A list of disjunctive conditions applied to
`Query._andalso`.
"""
self._default_cond = self._orelse if orelse else self._andalso
"""
Whether to use conjunctive or disjunctive conditions by
default.
"""
# The aggregate counter-parts of the above.
self._agg_andalso, self._agg_orelse = [], []
if orelse:
self._agg_default_cond = self._agg_orelse
else:
self._agg_default_cond = self._agg_andalso
def aggregate(
self, **kw)
This is just like play_player
, except the search
parameters are applied to aggregate statistics.
For example, to retrieve all quarterbacks who passed for at least 4000 yards in the 2012 season:
#!python
q = Query(db).game(season_year=2012, season_type='Regular')
q.aggregate(passing_yds__ge=4000)
for pp in q.as_aggregate():
print pp.player, pp.passing_yds
Aggregate results can also be sorted:
#!python
for pp in q.sort('passing_yds').as_aggregate():
print pp.player, pp.passing_yds
Note that this method can only be used with
as_aggregate
. Use with any of the other
as_*
methods will result in an assertion error. Note
though that regular criteria can still be specified with
game
, play
, etc. (Regular criteria
restrict what to aggregate while aggregate criteria restrict
aggregated results.)
def aggregate(self, **kw):
"""
This is just like `nfldb.Query.play_player`, except the search
parameters are applied to aggregate statistics.
For example, to retrieve all quarterbacks who passed for at
least 4000 yards in the 2012 season:
#!python
q = Query(db).game(season_year=2012, season_type='Regular')
q.aggregate(passing_yds__ge=4000)
for pp in q.as_aggregate():
print pp.player, pp.passing_yds
Aggregate results can also be sorted:
#!python
for pp in q.sort('passing_yds').as_aggregate():
print pp.player, pp.passing_yds
Note that this method can **only** be used with
`nfldb.Query.as_aggregate`. Use with any of the other
`as_*` methods will result in an assertion error. Note
though that regular criteria can still be specified with
`nfldb.Query.game`, `nfldb.Query.play`, etc. (Regular criteria
restrict *what to aggregate* while aggregate criteria restrict
*aggregated results*.)
"""
_append_conds(self._agg_default_cond, types.PlayPlayer, kw)
return self
def andalso(
self, *conds)
Adds the list of Query
objects in conds
to this
query's list of conjunctive conditions.
def andalso(self, *conds):
"""
Adds the list of `nfldb.Query` objects in `conds` to this
query's list of conjunctive conditions.
"""
self._andalso += conds
return self
def as_aggregate(
self)
Executes the query and returns the results as aggregated
PlayPlayer
objects. This method is meant to be a more
restricted but much faster version of aggregate
.
Namely, this method uses PostgreSQL to compute the aggregate
statistics while aggregate
computes them in Python
code.
If any sorting criteria is specified, it is applied to the aggregate player values only.
def as_aggregate(self):
"""
Executes the query and returns the results as aggregated
`nfldb.PlayPlayer` objects. This method is meant to be a more
restricted but much faster version of `nfldb.aggregate`.
Namely, this method uses PostgreSQL to compute the aggregate
statistics while `nfldb.aggregate` computes them in Python
code.
If any sorting criteria is specified, it is applied to the
aggregate *player* values only.
"""
class AggPP (types.PlayPlayer):
@classmethod
def _sql_field(cls, name, aliases=None):
if name in cls._derived_combined:
fields = cls._derived_combined[name]
fields = [cls._sql_field(f, aliases=aliases) for f in fields]
return ' + '.join(fields)
elif name == 'points':
fields = ['(%s * %d)' % (cls._sql_field(f, aliases=aliases), pval)
for f, pval in cls._point_values]
return ' + '.join(fields)
else:
sql = super(AggPP, cls)._sql_field(name, aliases=aliases)
return 'SUM(%s)' % sql
joins = ''
results = []
with Tx(self._db) as cur:
for ent in self._entities():
if ent is types.PlayPlayer:
continue
joins += types.PlayPlayer._sql_join_to_all(ent)
sum_fields = types._player_categories.keys() \
+ AggPP._sql_tables['derived']
select_sum_fields = AggPP._sql_select_fields(sum_fields)
where = self._sql_where(cur)
having = self._sql_where(cur, aggregate=True)
q = '''
SELECT
play_player.player_id AS play_player_player_id, {sum_fields}
FROM play_player
{joins}
WHERE {where}
GROUP BY play_player.player_id
HAVING {having}
{order}
'''.format(
sum_fields=', '.join(select_sum_fields),
joins=joins,
where=sql.ands(where),
having=sql.ands(having),
order=self._sorter(AggPP).sql(),
)
init = AggPP.from_row_dict
cur.execute(q)
for row in cur.fetchall():
results.append(init(self._db, row))
return results
def as_drives(
self)
Executes the query and returns the results as a list of
Drive
objects.
def as_drives(self):
"""
Executes the query and returns the results as a list of
`nfldb.Drive` objects.
"""
self._assert_no_aggregate()
results = []
with Tx(self._db, factory=tuple_cursor) as cursor:
q = self._make_join_query(cursor, types.Drive)
cursor.execute(q)
for row in cursor.fetchall():
results.append(types.Drive.from_row_tuple(self._db, row))
return results
def as_games(
self)
Executes the query and returns the results as a list of
Game
objects.
def as_games(self):
"""
Executes the query and returns the results as a list of
`nfldb.Game` objects.
"""
self._assert_no_aggregate()
results = []
with Tx(self._db, factory=tuple_cursor) as cursor:
q = self._make_join_query(cursor, types.Game)
cursor.execute(q)
for row in cursor.fetchall():
results.append(types.Game.from_row_tuple(self._db, row))
return results
def as_play_players(
self)
Executes the query and returns the results as a list of
nlfdb.PlayPlayer
objects.
This provides a way to access player statistics directly
by bypassing play data. Usually the results of this method
are passed to aggregate
. It is recommended to use
aggregate
and as_aggregate
when
possible, since it is significantly faster to sum statistics in
the database as opposed to Python.
def as_play_players(self):
"""
Executes the query and returns the results as a list of
`nlfdb.PlayPlayer` objects.
This provides a way to access player statistics directly
by bypassing play data. Usually the results of this method
are passed to `nfldb.aggregate`. It is recommended to use
`nfldb.Query.aggregate` and `nfldb.Query.as_aggregate` when
possible, since it is significantly faster to sum statistics in
the database as opposed to Python.
"""
self._assert_no_aggregate()
results = []
with Tx(self._db, factory=tuple_cursor) as cursor:
init = types.PlayPlayer.from_row_tuple
q = self._make_join_query(cursor, types.PlayPlayer)
cursor.execute(q)
for row in cursor.fetchall():
results.append(init(self._db, row))
return results
def as_players(
self)
Executes the query and returns the results as a list of
Player
objects.
def as_players(self):
"""
Executes the query and returns the results as a list of
`nfldb.Player` objects.
"""
self._assert_no_aggregate()
results = []
with Tx(self._db) as cursor:
q = self._make_join_query(cursor, types.Player)
cursor.execute(q)
for row in cursor.fetchall():
results.append(types.Player.from_row_dict(self._db, row))
return results
def as_plays(
self, fill=True)
Executes the query and returns the results as a dictionary
of nlfdb.Play
objects that don't have the play_player
attribute filled. The keys of the dictionary are play id
tuples with the spec (gsis_id, drive_id, play_id)
.
The primary key membership SQL expression is also returned.
def as_plays(self, fill=True):
"""
Executes the query and returns the results as a dictionary
of `nlfdb.Play` objects that don't have the `play_player`
attribute filled. The keys of the dictionary are play id
tuples with the spec `(gsis_id, drive_id, play_id)`.
The primary key membership SQL expression is also returned.
"""
def make_pid(play):
return (play.gsis_id, play.drive_id, play.play_id)
self._assert_no_aggregate()
# This is pretty terrifying.
# Apparently PostgreSQL can change the order of rows returned
# depending on the columns selected. So e.g., if you sort by `down`
# and limit to 20 results, you might get a different 20 plays if
# you change which columns you're selecting.
# This is pertinent here because if we're filling plays with player
# statistics, then we are assuming that this order never changes.
# To make the ordering consistent, we add the play's primary key to
# the existing sort criteria, which guarantees that the sort will
# always be the same.
# (We are careful not to override the user specified
# `self._sort_exprs`.)
#
# That was a lie. We override the user settings if the user asks
# to sort by `gsis_id`, `drive_id` or `play_id`.
consistent = [(c, 'asc') for c in ['gsis_id', 'drive_id', 'play_id']]
sorter = Sorter(types.Play, self._sort_exprs, self._limit)
sorter.add_exprs(*consistent)
if not fill:
results = []
with Tx(self._db, factory=tuple_cursor) as cursor:
init = types.Play.from_row_tuple
q = self._make_join_query(cursor, types.Play, sorter=sorter)
cursor.execute(q)
for row in cursor.fetchall():
results.append(init(self._db, row))
return results
else:
plays = OrderedDict()
with Tx(self._db, factory=tuple_cursor) as cursor:
init_play = types.Play.from_row_tuple
q = self._make_join_query(cursor, types.Play, sorter=sorter)
cursor.execute(q)
for row in cursor.fetchall():
play = init_play(self._db, row)
play._play_players = []
plays[make_pid(play)] = play
# Run the above query *again* as a subquery.
# This time, only fetch the primary key, and use that to
# fetch all the `play_player` records in one swoop.
aliases = {'play_player': 'pp'}
ids = self._make_join_query(cursor, types.Play,
only_prim=True, sorter=sorter)
from_tables = types.PlayPlayer._sql_from(aliases=aliases)
columns = types.PlayPlayer._sql_select_fields(
fields=types.PlayPlayer.sql_fields(), aliases=aliases)
q = '''
SELECT {columns} {from_tables}
WHERE (pp.gsis_id, pp.drive_id, pp.play_id) IN ({ids})
'''.format(columns=', '.join(columns),
from_tables=from_tables, ids=ids)
init_pp = types.PlayPlayer.from_row_tuple
cursor.execute(q)
for row in cursor.fetchall():
pp = init_pp(self._db, row)
plays[make_pid(pp)]._play_players.append(pp)
return plays.values()
def drive(
self, **kw)
Specify search criteria for a drive. The possible fields
correspond to columns in the drive
table (or derived
columns). They are documented as instance variables in the
Drive
class.
Please see the documentation for Query
for examples on
how to specify search criteria.
def drive(self, **kw):
"""
Specify search criteria for a drive. The possible fields
correspond to columns in the `drive` table (or derived
columns). They are documented as instance variables in the
`nfldb.Drive` class.
Please see the documentation for `nfldb.Query` for examples on
how to specify search criteria.
"""
_append_conds(self._default_cond, types.Drive, kw)
return self
def game(
self, **kw)
Specify search criteria for an NFL game. The possible fields
correspond to columns in the game
table (or derived columns).
They are documented as instance variables in the Game
class. Additionally, there are some special fields that provide
convenient access to common conditions:
- team - Find games that the team given played in, regardless of whether it is the home or away team.
Please see the documentation for Query
for examples on
how to specify search criteria.
Please open an issue if you can think of other special fields to add.
def game(self, **kw):
"""
Specify search criteria for an NFL game. The possible fields
correspond to columns in the `game` table (or derived columns).
They are documented as instance variables in the `nfldb.Game`
class. Additionally, there are some special fields that provide
convenient access to common conditions:
* **team** - Find games that the team given played in, regardless
of whether it is the home or away team.
Please see the documentation for `nfldb.Query` for examples on
how to specify search criteria.
Please
[open an issue](https://github.com/BurntSushi/nfldb/issues/new)
if you can think of other special fields to add.
"""
if 'team' in kw:
team = kw.pop('team')
ors = {'home_team': team, 'away_team': team}
self.andalso(Query(self._db, orelse=True).game(**ors))
_append_conds(self._default_cond, types.Game, kw)
return self
def limit(
self, count)
Limits the number of results to the integer count
. If count
is
0
(the default), then no limiting is done.
See the documentation for sort
for an example on how
to combine it with limit
to get results quickly.
def limit(self, count):
"""
Limits the number of results to the integer `count`. If `count` is
`0` (the default), then no limiting is done.
See the documentation for `nfldb.Query.sort` for an example on how
to combine it with `nfldb.Query.limit` to get results quickly.
"""
self._limit = count
return self
def orelse(
self, *conds)
Adds the list of Query
objects in conds
to this
query's list of disjunctive conditions.
def orelse(self, *conds):
"""
Adds the list of `nfldb.Query` objects in `conds` to this
query's list of disjunctive conditions.
"""
self._orelse += conds
return self
def play(
self, **kw)
Specify search criteria for a play. The possible fields
correspond to columns in the play
or play_player
tables (or
derived columns). They are documented as instance variables in
the Play
and PlayPlayer
classes. Additionally,
the fields listed on the
statistical categories
wiki page may be used. That includes both play
and
player
statistical categories.
Please see the documentation for Query
for examples on
how to specify search criteria.
def play(self, **kw):
"""
Specify search criteria for a play. The possible fields
correspond to columns in the `play` or `play_player` tables (or
derived columns). They are documented as instance variables in
the `nfldb.Play` and `nfldb.PlayPlayer` classes. Additionally,
the fields listed on the
[statistical categories](http://goo.gl/1qYG3C)
wiki page may be used. That includes **both** `play` and
`player` statistical categories.
Please see the documentation for `nfldb.Query` for examples on
how to specify search criteria.
"""
_append_conds(self._default_cond, types.Play, kw)
return self
def play_player(
self, **kw)
Specify search criteria for individual play player statistics.
The allowed fields are the columns in the play_player
table. They are documented as instance variables in the
PlayPlayer
class. Additionally, the fields listed on
the statistical categories wiki page
may be used. (Only the player
statistical categories.)
This method differs from play
in that it can be
used to select for individual player statistics in a play. In
particular, there are zero or more player statistics for
every play.
def play_player(self, **kw):
"""
Specify search criteria for individual play player statistics.
The allowed fields are the columns in the `play_player`
table. They are documented as instance variables in the
`nfldb.PlayPlayer` class. Additionally, the fields listed on
the [statistical categories](http://goo.gl/1qYG3C) wiki page
may be used. (Only the `player` statistical categories.)
This method differs from `nfldb.Query.play` in that it can be
used to select for individual player statistics in a play. In
particular, there are *zero or more* player statistics for
every play.
"""
# Technically, it isn't necessary to handle derived fields manually
# since their SQL can be generated automatically, but it can be
# much faster to express them in terms of boolean logic with other
# fields rather than generate them.
for field, value in kw.items():
nosuff = _no_comp_suffix(field)
suff = _comp_suffix(field)
def replace_or(*fields):
q = Query(self._db, orelse=True)
ors = dict([('%s__%s' % (f, suff), value) for f in fields])
self.andalso(q.play_player(**ors))
if nosuff in types.PlayPlayer._derived_combined:
replace_or(*types.PlayPlayer._derived_combined[nosuff])
kw.pop(field)
# Now add the rest of the query.
_append_conds(self._default_cond, types.PlayPlayer, kw)
return self
def player(
self, **kw)
Specify search criteria for a player. The possible fields
correspond to columns in the player
table (or derived
columns). They are documented as instance variables in the
Player
class.
Please see the documentation for Query
for examples on
how to specify search criteria.
def player(self, **kw):
"""
Specify search criteria for a player. The possible fields
correspond to columns in the `player` table (or derived
columns). They are documented as instance variables in the
`nfldb.Player` class.
Please see the documentation for `nfldb.Query` for examples on
how to specify search criteria.
"""
_append_conds(self._default_cond, types.Player, kw)
return self
def show_where(
self, aggregate=False)
Returns an approximate WHERE clause corresponding to the
criteria specified in self
. Note that the WHERE clause given
is never explicitly used for performance reasons, but one hopes
that it describes the criteria in self
.
If aggregate
is True
, then aggregate criteria for the
play
and play_player
tables is shown with aggregate
functions applied.
def show_where(self, aggregate=False):
"""
Returns an approximate WHERE clause corresponding to the
criteria specified in `self`. Note that the WHERE clause given
is never explicitly used for performance reasons, but one hopes
that it describes the criteria in `self`.
If `aggregate` is `True`, then aggregate criteria for the
`play` and `play_player` tables is shown with aggregate
functions applied.
"""
with Tx(self._db) as cur:
return self._sql_where(cur, aggregate=aggregate)
return ''
def sort(
self, exprs)
Specify sorting criteria for the result set returned by
using sort expressions. A sort expression is a tuple with
two elements: a field to sort by and the order to use. The
field should correspond to an attribute of the objects you're
returning and the order should be asc
for ascending (smallest
to biggest) or desc
for descending (biggest to smallest).
For example, ('passing_yds', 'desc')
would sort plays by the
number of passing yards in the play, with the biggest coming
first.
Remember that a sort field must be an attribute of the
results being returned. For example, you can't sort plays by
home_score
, which is an attribute of a Game
object.
If you require this behavior, you will need to do it in Python
with its sorted
built in function. (Or alternatively, use
two separate queries if the result set is large.)
You may provide multiple sort expressions. For example,
[('gsis_id', 'asc'), ('time', 'asc'), ('play_id', 'asc')]
would sort plays in the order in which they occurred within
each game.
exprs
may also just be a string specifying a single
field which defaults to a descending order. For example,
sort('passing_yds')
sorts plays by passing yards in
descending order.
If exprs
is set to the empty list, then sorting will be
disabled for this query.
Note that sorting criteria can be combined with
limit
to limit results which can dramatically
speed up larger searches. For example, to fetch the top 10
passing plays in the 2012 season:
#!python
q = Query(db).game(season_year=2012, season_type='Regular')
q.sort('passing_yds').limit(10)
for p in q.as_plays():
print p
A more naive approach might be to fetch all plays and sort them with Python:
#!python
q = Query(db).game(season_year=2012, season_type='Regular')
plays = q.as_plays()
plays = sorted(plays, key=lambda p: p.passing_yds, reverse=True)
for p in plays[:10]:
print p
But this is over 43 times slower on my machine than using
sort
and limit
. (The performance
difference is due to making PostgreSQL perform the search and
restricting the number of results returned to process.)
def sort(self, exprs):
"""
Specify sorting criteria for the result set returned by
using sort expressions. A sort expression is a tuple with
two elements: a field to sort by and the order to use. The
field should correspond to an attribute of the objects you're
returning and the order should be `asc` for ascending (smallest
to biggest) or `desc` for descending (biggest to smallest).
For example, `('passing_yds', 'desc')` would sort plays by the
number of passing yards in the play, with the biggest coming
first.
Remember that a sort field must be an attribute of the
results being returned. For example, you can't sort plays by
`home_score`, which is an attribute of a `nfldb.Game` object.
If you require this behavior, you will need to do it in Python
with its `sorted` built in function. (Or alternatively, use
two separate queries if the result set is large.)
You may provide multiple sort expressions. For example,
`[('gsis_id', 'asc'), ('time', 'asc'), ('play_id', 'asc')]`
would sort plays in the order in which they occurred within
each game.
`exprs` may also just be a string specifying a single
field which defaults to a descending order. For example,
`sort('passing_yds')` sorts plays by passing yards in
descending order.
If `exprs` is set to the empty list, then sorting will be
disabled for this query.
Note that sorting criteria can be combined with
`nfldb.Query.limit` to limit results which can dramatically
speed up larger searches. For example, to fetch the top 10
passing plays in the 2012 season:
#!python
q = Query(db).game(season_year=2012, season_type='Regular')
q.sort('passing_yds').limit(10)
for p in q.as_plays():
print p
A more naive approach might be to fetch all plays and sort them
with Python:
#!python
q = Query(db).game(season_year=2012, season_type='Regular')
plays = q.as_plays()
plays = sorted(plays, key=lambda p: p.passing_yds, reverse=True)
for p in plays[:10]:
print p
But this is over **43 times slower** on my machine than using
`nfldb.Query.sort` and `nfldb.Query.limit`. (The performance
difference is due to making PostgreSQL perform the search and
restricting the number of results returned to process.)
"""
self._sort_exprs = exprs
return self
class Team
Represents information about an NFL team. This includes its standard three letter abbreviation, city and mascot name.
class Team (object):
"""
Represents information about an NFL team. This includes its
standard three letter abbreviation, city and mascot name.
"""
# BUG: If multiple databases are used with different team information,
# this class won't behave correctly since it's using a global cache.
__slots__ = ['team_id', 'city', 'name']
__cache = defaultdict(dict)
def __new__(cls, db, abbr):
abbr = nfldb.team.standard_team(abbr)
if abbr in Team.__cache:
return Team.__cache[abbr]
return object.__new__(cls)
def __init__(self, db, abbr):
"""
Introduces a new team given an abbreviation and a database
connection. The database connection is used to retrieve other
team information if it isn't cached already. The abbreviation
given is passed to `nfldb.standard_team` for you.
"""
if hasattr(self, 'team_id'):
# Loaded from cache.
return
self.team_id = nfldb.team.standard_team(abbr)
"""
The unique team identifier represented as its standard
2 or 3 letter abbreviation.
"""
self.city = None
"""
The city where this team resides.
"""
self.name = None
"""
The full "mascot" name of this team.
"""
if self.team_id not in Team.__cache:
with Tx(db) as cur:
cur.execute('SELECT * FROM team WHERE team_id = %s',
(self.team_id,))
row = cur.fetchone()
self.city = row['city']
self.name = row['name']
Team.__cache[self.team_id] = self
def __str__(self):
return '%s %s' % (self.city, self.name)
def __conform__(self, proto):
if proto is ISQLQuote:
return AsIs("'%s'" % self.team_id)
return None
Ancestors (in MRO)
- Team
- __builtin__.object
Instance variables
var city
The city where this team resides.
var name
The full "mascot" name of this team.
var team_id
The unique team identifier represented as its standard 2 or 3 letter abbreviation.
Methods
def __init__(
self, db, abbr)
Introduces a new team given an abbreviation and a database
connection. The database connection is used to retrieve other
team information if it isn't cached already. The abbreviation
given is passed to standard_team
for you.
def __init__(self, db, abbr):
"""
Introduces a new team given an abbreviation and a database
connection. The database connection is used to retrieve other
team information if it isn't cached already. The abbreviation
given is passed to `nfldb.standard_team` for you.
"""
if hasattr(self, 'team_id'):
# Loaded from cache.
return
self.team_id = nfldb.team.standard_team(abbr)
"""
The unique team identifier represented as its standard
2 or 3 letter abbreviation.
"""
self.city = None
"""
The city where this team resides.
"""
self.name = None
"""
The full "mascot" name of this team.
"""
if self.team_id not in Team.__cache:
with Tx(db) as cur:
cur.execute('SELECT * FROM team WHERE team_id = %s',
(self.team_id,))
row = cur.fetchone()
self.city = row['city']
self.name = row['name']
Team.__cache[self.team_id] = self
class Tx
Tx is a with
compatible class that abstracts a transaction given
a connection. If an exception occurs inside the with
block, then
rollback is automatically called.
Tx blocks can be nested inside other Tx blocks. Nested Tx blocks never commit or rollback a transaction. Instead, the exception is passed along to the caller. Only the outermost transaction will commit or rollback the entire transaction.
Use it like so:
#!python
with Tx(conn) as cursor:
...
Which is meant to be roughly equivalent to the following:
#!python
with conn:
with conn.cursor() as curs:
...
This should only be used when you're running SQL queries directly. (Or when interfacing with another part of the API that requires a database cursor.)
class Tx (object):
"""
Tx is a `with` compatible class that abstracts a transaction given
a connection. If an exception occurs inside the `with` block, then
rollback is automatically called.
Tx blocks can be nested inside other Tx blocks. Nested Tx blocks
never commit or rollback a transaction. Instead, the exception is
passed along to the caller. Only the outermost transaction will
commit or rollback the entire transaction.
Use it like so:
#!python
with Tx(conn) as cursor:
...
Which is meant to be roughly equivalent to the following:
#!python
with conn:
with conn.cursor() as curs:
...
This should only be used when you're running SQL queries directly.
(Or when interfacing with another part of the API that requires
a database cursor.)
"""
def __init__(self, psycho_conn, name=None, factory=None):
"""
`psycho_conn` is a DB connection returned from `nfldb.connect`,
`name` is passed as the `name` argument to the cursor
constructor (for server-side cursors), and `factory` is passed
as the `cursor_factory` parameter to the cursor constructor.
Note that the default cursor factory is
`psycopg2.extras.RealDictCursor`. However, using
`psycopg2.extensions.cursor` (the default tuple cursor) can be
much more efficient when fetching large result sets.
"""
tstatus = psycho_conn.get_transaction_status()
self.__name = name
self.__nested = tstatus == TRANSACTION_STATUS_INTRANS
self.__conn = psycho_conn
self.__cursor = None
self.__factory = factory
if self.__factory is None:
self.__factory = RealDictCursor
def __enter__(self):
# No biscuits for the psycopg2 author. Changed the public API in
# 2.5 in a very very subtle way.
# In 2.4, apparently `name` cannot be `None`. Why? I don't know.
if self.__name is None:
self.__cursor = self.__conn.cursor(cursor_factory=self.__factory)
else:
self.__cursor = self.__conn.cursor(self.__name, self.__factory)
c = self.__cursor
if _SHOW_QUERIES:
class _ (object):
def execute(self, *args, **kwargs):
global _NUM_QUERIES
_NUM_QUERIES += 1
c.execute(*args, **kwargs)
print(c.query, file=sys.stderr, end='\n\n')
def __getattr__(self, k):
return getattr(c, k)
return _()
else:
return c
def __exit__(self, typ, value, traceback):
if not self.__cursor.closed:
self.__cursor.close()
if typ is not None:
if not self.__nested:
self.__conn.rollback()
return False
else:
if not self.__nested:
self.__conn.commit()
return True
Ancestors (in MRO)
- Tx
- __builtin__.object
Methods
def __init__(
self, psycho_conn, name=None, factory=None)
psycho_conn
is a DB connection returned from connect
,
name
is passed as the name
argument to the cursor
constructor (for server-side cursors), and factory
is passed
as the cursor_factory
parameter to the cursor constructor.
Note that the default cursor factory is
psycopg2.extras.RealDictCursor
. However, using
psycopg2.extensions.cursor
(the default tuple cursor) can be
much more efficient when fetching large result sets.
def __init__(self, psycho_conn, name=None, factory=None):
"""
`psycho_conn` is a DB connection returned from `nfldb.connect`,
`name` is passed as the `name` argument to the cursor
constructor (for server-side cursors), and `factory` is passed
as the `cursor_factory` parameter to the cursor constructor.
Note that the default cursor factory is
`psycopg2.extras.RealDictCursor`. However, using
`psycopg2.extensions.cursor` (the default tuple cursor) can be
much more efficient when fetching large result sets.
"""
tstatus = psycho_conn.get_transaction_status()
self.__name = name
self.__nested = tstatus == TRANSACTION_STATUS_INTRANS
self.__conn = psycho_conn
self.__cursor = None
self.__factory = factory
if self.__factory is None:
self.__factory = RealDictCursor


STATION 5 of 6
SDE4 utilizes a novel approach to offer thermally comfortable spaces to occupants while saving more energy, this system is called the hybrid cooling (HC). It is a result of intensive simulation and experimental research by building scientists at NUS which benefits from the fact that human body can still feel comfortable if the temperature is higher e.g. 28 degrees, if air movement is elevated. This is achieved by coupling ceiling fans with the air conditioning system.


Copy 📋
New one
Want to add your own excuses?
Dm me
Made by Ayush Shah :)
The Ultimate Excuse Corner
×
Late to work? Crossed your deadline? Can't make it to the party? Need an excuse to escape from going to someone's boring party? Or you just want to say no to anything.
Don't worry, I got you covered.


InsiderDelegate
public protocol InsiderDelegate: AnyObject
The Insider delegate protocol.
-
This method will be called on delegate for
send
commandDeclaration
Swift
optional func insider(_ insider: Insider, didReceiveRemote message: InsiderMessage?)
Parameters
insider
instance of Insider class
params
request params
-
This method will be called on delegate for
sendAndWaitForResponse
commandDeclaration
Swift
optional func insider(_ insider: Insider, returnResponseMessageForRemote message: InsiderMessage?) -> InsiderMessage?
Parameters
insider
instance of Insider class
params
request params
Return Value
return params
-
This method will be called on delegate for
notification
commandDeclaration
Swift
optional func insider(_ insider: Insider, didSendNotificationWith message: InsiderMessage?)
Parameters
insider
instance of Insider class
params
request params sent in notification
-
This method will be called on delegate for
systemInfo
commandDeclaration
Swift
optional func insider(_ insider: Insider, didReturn systemInfo: InsiderMessage?)
Parameters
insider
instance of Insider class
systemInfo
returned system information
-
This method is caled when a new directory is created in sandbox
Declaration
Swift
optional func insider(_ insider: Insider, didCreateDirectoryAt path: String)
Parameters
insider
instance of Insider class
path
path to the new created directory
-
This method is called when an item is removed from sandbox
Declaration
Swift
optional func insider(_ insider: Insider, didDeleteItemAt path: String)
Parameters
insider
instance of Insider class
path
path of the removed item
-
This method is called when an item is downloaded from sandbox
Declaration
Swift
optional func insider(_ insider: Insider, didDownloadFileAt path: String)
Parameters
insider
instance of Insider class
path
path to the downloaded item
-
This method is called when an item is moved in sandbox
Declaration
Swift
optional func insider(_ insider: Insider, didMoveItem fromPath: String, to path: String)
Parameters
insider
instance of Insider class
fromPath
initial path to the item
toPath
path to the item after it was moved
-
This method is called when an item is uploaded to sandbox
Declaration
Swift
optional func insider(_ insider: Insider, didUploadFileAt path: String)
Parameters
insider
instance of Insider class
path
path to the uploaded item


Through the use of the training mode and tutorial provided, the user can familiarize how to solve the puzzle. Basic technique is explained in the tutorial while the players can familiarize cracking codes in easy and hard difficulty in the training mode.
Through the free roam mode, the users are able to roam around using GPS or global positioning system and decided freely which location to invade and adding a twist with the use of Augmented Reality.
By playing the Objective mode, the user is required to find a specific location to invade and crack the code. The name of the location will be provided, but directions is up to the player to find. In this way, the user is required to interact around the area to ask for directions.


| CONTENTS | PREV | NEXT |
Appendix 3:
In a class diagram, we visually distinguish the different kinds of Java entities, as follows:
The interface: A rounded rectangle
Most of these elements are shown below. The class or interface being described in the current chapter is shaded grey (this is not applicable for package class diagrams). A solid line represents extends , while a dotted line represents implements .


// Using standard letters
jQuery • TodoMVC // Title on the tab
//
//
// Three stylesheets is interesting, I'm not sure why it'd need more than 1.
// The heart of the app is right here, from the title to the bottom of the UL.
todos
// Placeholder text, autofocus attribute is nifty seem
// Using "section" as the markup instead of div
// Curious, I'd have just made this a button, im not sure the advantage of making a 1 item checkbox
// Not sure where this label appears
// Just like in PJS, here's where new elements will be added.
// Still has a footer, even if its never used?
// 5 more scripts loaded here! It looks like node is core, and then jQuery, Handlebars, and 'director' are used extra.


--- layout: page title: Knowledge Topics class: blog sitemap: priority: 0.7 changefreq: monthly lastmod: 2013-08-29T12:49:30-05:00 ---
{{ post.excerpt }}
Published {{ post.date | date: "%B %d, %Y" }}


{% include shared/head.html %}
{% include shared/gtm.html %} {% include shared/nav.html %} {% include homepage/header.html %} {% include homepage/sections.html %} {% include shared/cta_to_hire_us.html %} {% include shared/footer.html %} {% include shared/js.html %} {% include shared/js_animation.html %}


Form Validation
Bootstrap Validation - Normal
For custom Bootstrap form validation messages, you’ll need to add the novalidate
boolean attribute to your <form>
.
Bootstrap Validation (Tooltips)
If your form layout allows it, you can swap the .{valid|invalid}-feedback
classes for .{valid|invalid}-tooltip
classes to display validation feedback in a styled tooltip.
Validation type
Parsley is a javascript form validation library. It helps you provide your users with feedback on their form submission before sending it to your server.
Range validation
Parsley is a javascript form validation library. It helps you provide your users with feedback on their form submission before sending it to your server.


Plane crashes into homes killing at least 24 in DR Congo
At least 25 people have died after a plane crashed into a residential area.
The plane destroyed homes as it landed, killing several people on the ground as well as 17 passengers on board.
It crashed shortly after takeoff today in the Democratic Republic of Congo’s eastern city of Goma.
Two people are believed to have survived the crash, including one member of crew.
They are being treated in hospital, according to the National Border Health Program.
Provincial governor Nzanzu Kasivita Carly said the 19-seater aircraft hit homes in the Mapendo district near Goma’s airport in North Kivu province shortly after ‘missing’ its takeoff.
Black smoke rose from the plane in the morning, whose wreckage could be seen amid destroyed homes as dozens of men tried to help with the rescue efforts.
Advertisement Advertisement
The smoke cleared as rescue workers carried bodies in stretchers and hundreds gathered at the scene.
Placide Kambale, a local pilot, said he took a taxi to the scene of the crash to help out. When he got there, the plane was on fire.
He said: ‘I called other young people from the neighborhood. They helped me to try to remove those who still moved.
‘We have managed to recover two that was quickly sent to the hospital.’
Joseph Makundi, coordinator of the North-Kivu civil protection, said that at least 25 people had been killed.
The Dornier 228-200 was owned by private carrier Busy Bee and was headed to Beni, about 220 miles north of Goma in the same province.
The UN mission in Congo said it sent an Emergency Crash and Rescue team with two fire engines to support Congolese authorities.
None of Congo’s commercial carriers, including Busy Bee, are allowed to fly into European Union airspace because of safety concerns.
Got a story for Metro.co.uk?
Get in touch with our news team by emailing us at<EMAIL_ADDRESS>For more stories like this, check our news page.


PDF documents can be created to display table data using the $pdf
service. By supplying the column definitions
and a dataset a table can automatically be created and a PDF can then be generated. Currently the service is limited
to creating PDFs from tables of rows and columns consisting of alphanumeric text only.
To create a document call the createTable
function on the $pdf
service. The createTable
function can take three parameters, the first an array of column definitions (which are explained below), the second
containing the data for each row and the third defining any additional configuration.
Below is an example of how to create the table and download the generated PDF.
In the code snippet above, the function will return an object containing the newly created document. The returned object
will have a download
function that will allow you to download the generated PDF.
Column definitions should be an array of objects, each object can contain the following properties:
100
) or a percentage
('30%'
). Additionally you can let the service determine the size using 'auto'
which
will take only the size required to display the data in the column, or '*'
which will stretch
to fill up any available space.
You can also provide several options to customize the PDF being generated. If not specified then default values will be used.
portrait
or landscape
.
title
- The title of the document.author
- The author of the document.subject
- The subject of the document contents.keywords
- Relevant keywords for the document.Below is the code used in the example above:
Note: This service can be used in an Angular application by importing the HybridModule.


I am interning with a lending firm now called PeopleFund, who focus on making loans, up to $250,000 to small businesses. I am grateful for my experiences with PeopleFund and Julia Dunn, whose recommendation letter is here.
Click to viewThis site is kept to update my stories


OCUnit - Script to Kill Simulator After Tests Run
I’m in the process of adding OCUnit tests to an existing iOS code base. Throughout this process, the biggest pain point I have encountered has been the simulator taking over the screen after an application test suite has run.
This behavior completely breaks the flow of red/green/refactor, especially when you are on a roll. Here is a typical round trip for me when testing using the default behavior:
- Make a code change
- Hit ⌘U to run the test suite
- Simulator appears and takes over the screen
- Tests complete in Xcode, which is now behind the simulator
- ⌘ + Tab back to Xcode to check the results, or ⌘ + Tab to the simulator, then ⌘ + Q to quit it
I solved this problem by running a script to close the simulator. To automate this, I hooked into Xcode’s behaviors functionality. Using behaviors, you can specify actions to run before and after testing runs.
Here is a breakdown of how I am now forcing the simulator to close when tests complete:
- Open a text file and add the following shell script
1 2 |
|
- Create a folder at the base level of your project named Scripts
- Save the text file into this folder and name it quitsim.sh
- Open a terminal session, cd to the new Scripts folder, and run the following command on the file to make it executable:
1
|
|
- Now, open Xcode, and go to preferences(⌘,)
- Go to the Behaviors tab
- Under the Testing category, select Succeeds
- Scroll to the bottom, and check the Run selection
- Select Choose Script … from the pull down menu
- Select the script you just saved into the Scripts folder
- Repeat this process for the Fails selection in the Testing category
You should now be able to run your test suite and have the simulator close upon completion or failure. This has removed a consistent annoyance for me and sped up my workflow just a little bit.
Every small improvement helps.


Let us know how we can improve freeCodeCamp
Male Female Other
In 50 words or more explain why you want to work with us?
Upload Police Clearance Certificate, Bank Statement and Medical Certifiates here: Attach Files


Ocala events
-
freestone realty northbrook il
Winnie The Pooh Car Accessories - Squidoo
Love Winnie the Pooh? If you would like to have Winnie the Pooh theme car, you might want to check out for some great Winnie the Pooh car accessories below.
http://www.squidoo.com/winnie-the-pooh-car-accessoriesDisney Winnie The Pooh Travel Set - car accessories - Mothercare
If your child loves Disney Winnie The Pooh then they will love the Disney Winnie The Pooh Travel Set! It comes complete with a travel neck pillow, seat belt pad .
http://www.mothercare.com/Disney-Winnie-The-Pooh-Travel-Set/232326,default,pd.htmlWinnie the Pooh Piglet Disney Car Side Window Sunshade - Pr ...
Winnie the Pooh Piglet Disney Car Side Window Sunshade - Pr . Part Finder · Deals & Rebates · Best Sellers · Tools & Equipment · Interior Accessories · Truck .
http://www.amazon.com/Winnie-Piglet-Disney-Window-Sunshade/dp/B001D4RQVI
bible verses with hills Upcoming events in Ocala
- Sponsored event
similac formula causing constipation
Apr 6 - Downtown Disney
marion county georgia tax assessment october fest in minnesota
i will survive stefanie Hottest Tickets
-
Apr 11queen ann drop leaf cocktail table
Amway Center -
Apr 28mens 3 inseam very short shorts
Amway Center -
Jun 25how to teach on online course
Bob Carr Performing Arts Center -
Mar 30nicola fitzharris newcastle upon tyne
Amway Center -
Aug 27giant bird of paradise seeds
Amway Center -
Jun 21tricare eligability for children
Amway Center -
Sep 13top 10 songsd of the week
Francis Field -
Mar 17john spence m d
St Augustine Amphitheatre -
Oct 8burnig a cd with a match
Amway Center -
May 4florida beach front rental july 25
Bob Carr Performing Arts Center
Ocala's best venues
-
Winnie the Pooh Bath Mitt for Baby | Bath Accessories | Disney Store
This Winnie the Pooh Bath Mitt features a sweet little embroidered face and Pooh Bear's signature red tee on a soft cotton bath mitt. Perfect for putting on puppet .
http://www.disneystore.com/bath-accessories-bed-bath-home-decor-winnie-the-pooh-bath-mitt-for-baby/mp/1315032/1000325/ -
Winnie the Pooh 11731 3D Air Freshener - Pooh Bear: Amazon.co ...
Winnie the Pooh 11731 3D Air Freshener - Pooh Bear: Amazon.co.uk: Car . and products, all at great prices, including car accessories, sat navs and car seats.
http://www.amazon.co.uk/Winnie-Pooh-11731-Air-Freshener/dp/B002MTDQIU -
Disney ''Winnie the Pooh'' Car Mats rubber (2 pcs): Amazon.co.uk ...
Disney ''Winnie the Pooh'' Car Mats rubber (2 pcs): Amazon.co.uk: Car . and products, all at great prices, including car accessories, sat navs and car seats.
http://www.amazon.co.uk/Disney-Winnie-Pooh-Mats-rubber/dp/B005BOMKX0the movie tavern in humble tex
98 people's favorite
-
Hello Kitty, Car Accessories items in Raylin's Store store on eBay!
Find a huge selection of Car Accessories, Seat Covers items .
http://stores.ebay.com/Raylins-Store -
TLC Family "Winnie the Pooh: Behind the Scenes"
The Winnie the Pooh TV shows and movies are classic. Meet the voices of Winnie the Pooh and his friends only at HowStuffWorks. . A fantasy world inhabited only by cars is the setting of the Disney/Pixar collaboration "Cars." Learn about the .
http://tlc.howstuffworks.com/family/how-winnie-the-pooh-works4.htm


FAQ
1) How can I use ObiKey?
Download "ObiKey Helper" extension for Google Chrome, then on a website click ObiKey extension icon and decide which permissions you want to give, and scan the QR code with your phone. Then you have the option to generate a random identity, or use one of your previous identities. If you approve, your data will be sent through a secure channel to your browser.
2) Which websites are supported?
You should either go to a website that supports ObiKey natively (they will let you know with a button, or a QR code), or download Google Chrome Plugin for ObiKey and use it in every website you visit.
3) Can I move my data to my new phone?
Unfortunately not yet, but we know how important this is. We are just trying to find the safest way.
4) Is it dangerous if someone scans my QR code?
If this is the QR code generated by your browser, they can see your email, but if they see your screen that is probably the least they know.
5) Which encryption are you using?
We are using AES encryption with 256-bit one-time keys. It will take around<PHONE_NUMBER>00000000000000000000000000000000000000000(10^56) years to crack this. Your local data is encrypted with your password, so if you put a password like 123123 brute-force will be much more easier, but that's only an issue if your phone gets physically stolen, online transactions are always secured with random 256-bit keys, even if you don't put a local password.
6) Can I use with mobile websites?
We support this kind of authorization, but developers need to implement this, it will take a bit time.
7) Can I use with applications?
We support this kind of authorization, but developers need to implement this, it will take a bit time.
8) Can I store my credit card info?
Very soon, depending on the feature requests we will prioritize what we must improve.
9) I heard you were open-source?
We are 100% open source, on will you can download all our code and build your own ObiKey server, also check if it suits your security standards.
More Questions?
Please visit us at ObiKey.com or drop us an email at<EMAIL_ADDRESS>thank you for your support!.


We all accept these phrases and those that are similar as being logically sound without question. If something comes naturally, then surely it must be right, and if it was going to happen anyway, then it must be good, right? Examples of these include our unreasonable acceptance that gut feelings are ignored at our own peril and a mother’s intuition is always right.
Knowingly or unknowingly, most of us believe in the importance and the inherent goodness of nature, but where does this belief come from and how reasonable is it? With many ‘unnatural’ technological advances like novel gene editing and implantable chips, the gravity of answering these questions cannot be understated. Levinovitz attempts to do exactly this in his book Natural: The Seductive Myth of Nature’s Goodness.
Levinovitz makes mention of how the inherent goodness of nature is consistent with most traditions over time. The Taoists of ancient China spoke of the significance of ziran, the “self-so”, which translates to something like ‘the spontaneous occurrences in the universe’. The Taoists believed that the spontaneous transformations (aka natural events) of the universe are not the product of any assertive desire or action, and for humans to interfere by asserting their will is to interrupt the ideal state of existence. This disruption isn’t without consequence.
Nature/natural plays an important role in Islam. Muslims believe that all humans are born with the fitrah (primordial nature) which can also be translated to natural disposition or tendency. Intrinsic to this is the ability to perceive the Oneness of God, and through adverse environmental influences and/or self-indulgence is this disposition influenced and contaminated.
Beyond the subscribers of ancient traditions and religious beliefs, nature seems to play an important role in most of our day to day lives, from the foods we eat and our behavioural habits to the use of natural analogies to provide support for our beliefs. The more naturally aligned our political, economic, and social systems are the better.
The issue with this, as Levinovitz rightfully points out, is that there are many natural occurring phenomena that are not good (maternal and infant mortality, disease etc), therefore using nature interchangeably with goodness is rife with contradictions. Befitting of a Professor of Religious Studies, he concludes that these terms are theological in nature (lool), whereby ‘Nature’ is another term for God and ‘natural’ is holy. Just as God’s Commandments cannot be disobeyed, he argues that Nature’s Commandments cannot be ignored.
I used to scorn at anti-vaxxers and didn’t understand their sentiment, however, after reading this book it has become apparent to me that we all subscribe to the axiom that is ‘what’s natural is good’, any lines that we draw seem somewhat arbitrary.  What seems to be happening is no different than the disagreements between religious fundamentalists (anti-vaxxers) and religious liberals (vaxxers[??]).
Natural births highlight how what’s natural is synonymous with doing what’s right. Proponents of natural births speak of the blissful days of yesteryear when children were born without the need for epidurals or invasive care. Many refer to animals and make mention of how following their instincts seem to work for them and also point to how women throughout history didn’t require medical assistance.
Unfortunately, it’s easy for the unexamined past to resemble a utopia, but with the slightest bit of inspection, we can come to learn that that's the furthest from the truth one. Maternal mortality in the UK in the early 18th century was as high as 1000 per 100,000 births and is now at 19 per 100,000 births (as of 2016), which can be largely attributed to transcending nature and implementing the ‘unnatural’ practices which saw advances in healthcare.
In the process of medicalising pregnancy to ensure safety, Levinovitz mentions how many mothers felt that this transformed a miracle into a disease and treated the miracle bearers as ill patients. The experience reduced pregnancy to a medical condition, described only in terms of risk and reduced the value of the experience into purely scientific metrics. In this respect, it’s easy to see why many mothers opt for natural births, even with the associated risks.
Levinovitz mentions how vanilla became a staple item in every household from when it was first brought to Europe after the invasion of the Americas, to the discovery of artificial insemination by the 12-year-old Edmond Albius some 200 years later.
This process of artificial insemination of vanilla is by hand-pollinating every individual flower, an endeavour so expensive that most vanilla is derived from petrochemicals. The idea that anything I put in my mouth is comes from petrochemicals is sickening, on the basis that it isn’t natural. However, after little reflection I realised that the alternative (artificial insemination) isn’t much of a ‘natural’ alternative and as Levinovitz points out, the history of food seems to be built on its unnatural manipulation e.g., heating, cooling, mixing etc. I tried to find out exactly where I draw this arbitrary line and had no success.
Producing vanilla from petrochemicals not only democratises it, but also limits the number of children on plantations labouring to make ‘natural’ vanilla so I can feel better about what I eat. Then I guess where I would draw my arbitrary line is at the point where my pursuit for what is natural causes harm to others and the environment.
Many around the world would leap at the opportunity to have access to cheap foods, even if its origin wasn’t ‘natural’ and many women around the global would give anything to have access to ‘unnatural’ neo-natal and maternal care. Levinovitz shares a story about when he spent time with the people of Huacaria, a native community in the Amazon, who were actively choosing to integrate fewer natural practices. An elderly Matsigenka women shared her gratitude for running water and another elderly man looked at Levinovitz with confusion when he was asked whether he was happy having electricity, responding yes as they could now see at night.
The modern dilemma of whether nature should be transcended seems to be a luxury problem that only those who live in comfort have to worry about. Tackling this requires gratitude for the unnatural advances that has provided us with the opportunity to have these discussions, and serious reflections on our personal relationship with nature.
Anyway, it’s a cool book, highly recommend.


Choose your favorite pizza!
Home Page
Index Two Page
DeltaV GitHub Page
Papa Vitos!
The various pizzas


Forum: CompilerIssue: SHARP-COMMA-CONFUSION
References: CLtL p. 356
Issue LOAD-TIME-EVAL
Category: CHANGE
Edit History: V1, 17 Oct 1988, Sandra Loosemore
V2, 30 Dec 1988, Sandra Loosemore (comments from Pitman)
Status: Proposal REMOVE passed Jan 89
Problem Description:
The way the read macro #, is defined in CLtL does not make it clear
that it can only appear inside of (implicitly) quoted structure to
guarantee consistent handling between the interpreter and the
compiler. Examples of inconsistent uses would be #, forms appearing
outside of a QUOTE that expand into list or symbol forms that could be
interpreted as code, or strings that could be interpreted as
documentation strings. Users are even more likely to be confused
because the wording in CLtL compares the behavior of #, to the special
form EVAL-WHEN, which must appear in a for-evaluation position.
#, also presents a serious problem to program-analyzing programs
because evaluation is tied to the reader, rather than the interpreter
or compiler. In theory, this could be handled by altering the read table
to have #, construct a "magic cookie" instead of performing read-time
evaluation and having the code walker examine quoted structures, but I've
never seen this actually done in practice.
Proposal SHARP-COMMA-CONFUSION:REMOVE:
Remove the #, read macro from the language.
Rationale:
Removing #, is simpler than trying to redefine it. Removing it from
the standard, rather than redefining it to mean something else (see
issue LOAD-TIME-EVAL), would allow implementations to continue to
provide it as an extension. (Although CLtL does not appear to
explicitly address the question of whether implementations may extend
the reader syntax, some implementations already provide sharp-sign
read macros other than those described in CLtL, such as #P syntax for
pathnames.)
Current Practice:
#, is not used very frequently, but the functionality it provides is
important in some advanced applications; one such application that has
been cited is CLOS. Maintainers of such applications have generally
expressed a willingness to give up #, only if a suitable alternative
is offered (see issue LOAD-TIME-EVAL).
PSL/PCLS has never bothered to implement #, correctly (it's treated
just like #.), and nobody has ever complained about it being broken.
Cost to implementors:
None.
Cost to users:
Because #, is used so infrequently, most users would probably not even
notice its absence.
Issue LOAD-TIME-EVAL proposes to add a special form that would provide
a somewhat cleaner mechanism for load-time evaluation.
It is also possible to use a global variable to get the similar effect as
#,, although this is sometimes awkward and carries a space and
performance penalty in many implementations.
Benefits:
The language specification is simplified by removing a peculiar
feature of the language that is accessible only through the reader.
Removing #, may also allow simpler strategies for implementing
compiled file loaders to be used.
Discussion:
If this proposal is rejected, the definition of #, in the standard will
still need to be clarified to indicate that #, can only appear in
quoted structure. It should probably also include some mention of the
problems that #, can cause code walkers.
Kent Pitman says:
I approve of the ideas being discussed, but ONLY contingent on
LOAD-TIME-VALUE being introduced.
I am optimistic that LOAD-TIME-EVAL will pass, and so I don't think this
will keep #, from passing, but:
- I want people who vote for this to realize the importance of voting
for LOAD-TIME-EVAL.
- On the off chance LOAD-TIME-EVAL doesn't pass, I want people to have
been warned that the consequences were severe for some major
applications.
- I want the records to reflect the actual rationale people should and
hopefully will be using to make these decisions.


Tory politician kicked out of party after 'offering to whip a woman on Twitter'
Councillor Steve Iles, who had been the Mayor of Medway, has been expelled from the Conservative Party over a series of inappropriate comments he made on social media
by Elaine McCahill, https://www.mirror.co.uk/authors/elaine-mccahill/A Tory politician has been kicked out of the party over making inappropriate sexual comments on social media.
The remarks made by the local politician included him 'offering to whip a woman on Twitter '.
Councillor Steve Iles, who had been Mayor of Medway, was expelled from the Conservative Party after the “thoroughly unacceptable” Facebook and Twitter posts emerged.
In one Facebook post, Iles, from Medway, Kent, posted a picture of a bacon sandwich with the caption: “I am proud to be English”.
Bosses felt the implication was that he was inferring that those who do not eat pork for religious reasons were somehow less English.
In another tweet, Iles responded to a woman who had shared a photo of herself posing in lingerie asking Mr Motivator to “whip me into shape”.
Iles replied: “Shape is fine, if I can help with the whipping…?”
He claimed that his account had been hacked, and has since deleted both his Facebook and Twitter accounts.
Iles has been a long-serving councillor in Strood North, Kent, and was elected as Medway’s deputy mayor this year having served as its mayor the year before.
While he will remain a local councillor, the Conservatives confirmed that Iles had been thrown out of the party.
Councillor Alan Jarrett, the Tory leader of Medway council, said: “As a result of thoroughly unacceptable posts on social media, Councillor Steve Iles has been expelled from the Conservative Party.
“The posts in question are completely contrary to the values of both the Medway Conservative Group and the Conservative Party nationally, and do not adhere to our strong commitment to diversity and inclusivity.
“I have met with Councillor Iles and explained that his actions are unacceptable, and as a consequence he will no longer enjoy the Conservative whip or be able to sit as a Conservative councillor.
“On a personal note, I sincerely regret any offence the actions of Councillor Iles may have caused.”
Medway council confirmed that no formal disciplinary proceedings have been triggered against Iles.
Mr Iles said: "I regret any offence that was caused and none was meant whatsoever."


A pasta dish will be available for free next week in one Dublin restaurant
by Nick FitzgeraldSprezzatura are giving away their coeliac friendly Buckwheat Fettuccine with Pesto dish all day for absolutely nothing on Monday, November 25
To mark their new gluten free addition to their menu, Sprezzatura are giving away a coeliac-friendly dish for absolutely nothing all day on Monday, November 25.
And the dish on offer is none other than Buckwheat Fettuccine with Pesto. As seen below, it looks simply delish.
Located at 5/6 Camden Market on Grantham Street, walk-ins will be welcomed throughout the day but bookings can also be made through the website sprezzatura.ie with payment taken by card only.
Going forward, the Italian restaurant will be featuring one gluten-free dish on its menu each week.
All thanks to head chef Alexandre Braz, the focus will be on great flavours and seasonal produce.
This is a dish surely not to miss for all you pasta lovers out there. See you on Monday!
To check out the restaurants full menu you can do so here.


public class ParenthesesBlock extends java.lang.Object implements IStatement
IStatement.StatementType
| Modifier and Type | Field and Description |
|---|---|
protected boolean |
allowSelect |
protected java.lang.String |
graph |
protected boolean |
optional |
protected java.util.List<IStatement> |
statements |
| Constructor and Description |
|---|
ParenthesesBlock(java.util.List<IStatement> statements,
java.lang.String graph)
constructor that takes a list of statements and a named graph
|
ParenthesesBlock(SplitQuery.SplitQueryIterator iterator)
Constrcutor that only takes an iterator to operate over
|
ParenthesesBlock(SplitQuery.SplitQueryIterator iterator,
boolean allowSelect)
Constructor that takes an iterator to construct the parentheses block from it.
|
ParenthesesBlock(java.lang.String block,
java.lang.String graph)
Default constructor with a single statementblock and possibly a named
graph on which it operates
|
| Modifier and Type | Method and Description |
|---|---|
IStatement |
clone() |
java.lang.String |
getGraph()
returns the graph on which this statement operates
|
IStatement.StatementType |
getType()
the types are describe in the StatementType definition
|
java.util.Set<java.lang.String> |
getUnknowns()
calculates the unknowns (on the fly) and returns them as a set
|
protected void |
parseBlock(SplitQuery.SplitQueryIterator iterator)
Parses a block between a '{' and a '}' token and returns the resulting block a single string
|
void |
replaceGraphStatements(java.lang.String newGraph)
this will propagate the replacement of ALL subsequent graph statements with the new
graph name.
|
void |
replaceGraphStatements(java.lang.String oldGraph,
java.lang.String newGraph)
this will propagate the replacement of ALL subsequent graph statements which are
equal to the oldGraph's name.
|
void |
setAllowSelect(boolean allowSelect) |
void |
setOptional(boolean optional) |
java.lang.String |
toString() |
protected java.util.List<IStatement> statements
protected java.lang.String graph
protected boolean allowSelect
protected boolean optional
public ParenthesesBlock(java.lang.String block, java.lang.String graph)
block
- the block between the parenthesesgraph
- the named graphpublic ParenthesesBlock(SplitQuery.SplitQueryIterator iterator, boolean allowSelect) throws InvalidSPARQLException
iterator
- the iterator over the queryallowSelect
- InvalidSPARQLException
- if the iterator reaches a state which is not compatible with what
would be expected if the iterator operates on a valid SPARQL querypublic ParenthesesBlock(SplitQuery.SplitQueryIterator iterator) throws InvalidSPARQLException
iterator
- the iteratorInvalidSPARQLException
- if the iterator reaches a state which is not compatible with what
would be expected if the iterator operates on a valid SPARQL querypublic ParenthesesBlock(java.util.List<IStatement> statements, java.lang.String graph)
statements
- the list of statementsgraph
- the named graph over which they operatepublic java.util.Set<java.lang.String> getUnknowns()
getUnknowns
in interface IStatement
protected void parseBlock(SplitQuery.SplitQueryIterator iterator) throws InvalidSPARQLException
iterator
- a valid SplitQueryIteratorInvalidSPARQLException
public void setAllowSelect(boolean allowSelect)
allowSelect
- this.allowSelect = allowSelectpublic void setOptional(boolean optional)
optional
- this.optional = optionalpublic java.lang.String toString()
toString
in interface IStatement
toString
in class java.lang.Object
public IStatement.StatementType getType()
IStatement
getType
in interface IStatement
public IStatement clone()
clone
in interface IStatement
clone
in class java.lang.Object
public void replaceGraphStatements(java.lang.String newGraph)
replaceGraphStatements
in interface IStatement
newGraph
- the name of the new graphpublic void replaceGraphStatements(java.lang.String oldGraph, java.lang.String newGraph)
replaceGraphStatements
in interface IStatement
oldGraph
- the name of tha graph that needs be replacednewGraph
- the new graph namepublic java.lang.String getGraph()
getGraph
in interface IStatement


{% extends 'base.html' %} {% block head %}
There was no page found at this location. Please check your URL and try again. If you think that this page is shown in error, please Contact us
Otherwise, you may find what you are looking for by searching the Pubs Warehouse
{% endblock main %} {% block url %}{{ request.url }}{% endblock %}


There are no requirements to display
{{req.name}}
{{req.capability.substring("tosca.capabilities.".length)}}
{{req.node.substring("tosca.nodes.".length)}}
{{req.relationship.substring("tosca.relationships.".length)}}
{{req.minOccurrences}},{{req.maxOccurrences}}


--- layout: page title: "About" description: "Hey, this is Hann." header-img: "img/about-bg.jpg" ---
THU电子狗一只，实习，毕设，阅读，旅行，永远在路上。
电子狗的生活是什么样子？举个栗子吧：
普通人眼中是这样的：
而我们眼中是这样的：
Don't you read Chinese? Why do you want to read English?
I am currently a senior undergraduate student at the Dept. of EE, Tsinghua University (B.Eng. expected July 2017), majoring in Electronic Information Science and Technology. And I'm going to study for my PhD. in the next 5 years at this department.
My research interests are mainly in Computer Vision and Machine Learning. I am skilled in C/C++, MATLAB, LaTeX, algorithm, deep learning and etc. and currently working as a part-time algorithm engineer at Ninebot & Segway Robotics.
During my days off work and school, I enjoy myself travelling around. Besides the fantastic sceneries of the destinations, reading a book while the train takes you far away is also a great pleasure to me. Please do say hello to me if you see someone reading books rather than playing with mobiles/tablets on trains (^_^).
Work hard to make a better world with artificial intelligence.