nfldb API documentation
Description: Module nfldb provides command line tools and a library for maintaining and querying a relational dat...

Top

# Module nfldb

Module nfldb provides command line tools and a library for maintaining and querying a relational database of play-by-play NFL data. The data is imported from nflgame, which in turn gets its data from a JSON feed on NFL.com's live GameCenter pages. This data includes, but is not limited to, game schedules, scores, rosters and play-by-play data for every preseason, regular season and postseason game dating back to 2009.

In theory, both `nfldb` and `nflgame` provide access to the same data. The difference is in the execution. In order to search data in nflgame, a large JSON file needs to be read from disk and loaded into Python data structures for each game. Conversely, nfldb's data is stored in a relational database, which can be searched and retrieved faster than nflgame by a few orders of magnitude. Moreover, the relational organization of data in nfldb allows for a convenient query interface to search NFL play data.

The database can be updated with real time data from active games by running the `nfldb-update` script included with this module as often as you like. Roster updates are done automatically at a minimum interval of 12 hours.

nfldb has comprehensive API documentation and a wiki with examples.

nfldb can be used in conjunction with nflvid to search and watch NFL game footage.

Show source.

    """
    Module nfldb provides command line tools and a library for maintaining
    and querying a relational database of play-by-play NFL data. The data
    is imported from [nflgame](https://github.com/BurntSushi/nflgame),
    which in turn gets its data from a JSON feed on NFL.com's live
    GameCenter pages. This data includes, but is not limited to, game
    schedules, scores, rosters and play-by-play data for every preseason,
    regular season and postseason game dating back to 2009.

    In theory, both `nfldb` and `nflgame` provide access to the same data.
    The difference is in the execution. In order to search data in nflgame,
    a large JSON file needs to be read from disk and loaded into Python
    data structures for each game. Conversely, nfldb's data is stored in
    a relational database, which can be searched and retrieved faster
    than nflgame by a few orders of magnitude. Moreover, the relational
    organization of data in nfldb allows for a convenient
    [query interface](http://goo.gl/Sd6MN2) to search NFL play data.

    The database can be updated with real time data from active games by
    running the `nfldb-update` script included with this module as often as
    you like. Roster updates are done automatically at a minimum interval
    of 12 hours.

    nfldb has [comprehensive API documentation](http://pdoc.burntsushi.net/nfldb)
    and a [wiki with examples](https://github.com/BurntSushi/nfldb/wiki).

    nfldb can be used in conjunction with
    [nflvid](https://pypi.python.org/pypi/nflvid)
    to
    [search and watch NFL game footage](http://goo.gl/1qSwJw).
    """
    from __future__ import absolute_import, division, print_function

    from nfldb.db import __pdoc__ as __db_pdoc__
    from nfldb.db import api_version, connect, now, set_timezone, schema_version
    from nfldb.db import Tx
    from nfldb.query import __pdoc__ as __query_pdoc__
    from nfldb.query import aggregate, current, Query, QueryOR
    from nfldb.team import standard_team
    from nfldb.types import __pdoc__ as __types_pdoc__
    from nfldb.types import stat_categories
    from nfldb.types import Category, Clock, Enums, Drive, FieldPosition, Game
    from nfldb.types import Play, Player, PlayPlayer, PossessionTime, Team
    from nfldb.version import __pdoc__ as __version_pdoc__
    from nfldb.version import __version__

    __pdoc__ = __db_pdoc__
    __pdoc__ = dict(__pdoc__, **__query_pdoc__)
    __pdoc__ = dict(__pdoc__, **__types_pdoc__)
    __pdoc__ = dict(__pdoc__, **__version_pdoc__)

    # Export selected identifiers from sub-modules.
    __all__ = [
        # nfldb.db
        'api_version', 'connect', 'now', 'set_timezone', 'schema_version',
        'Tx',

        # nfldb.query
        'aggregate', 'current', 'Query', 'QueryOR',

        # nfldb.team
        'standard_team',

        # nfldb.types
        'stat_categories',
        'Category', 'Clock', 'Enums', 'Drive', 'FieldPosition', 'Game',
        'Play', 'Player', 'PlayPlayer', 'PossessionTime', 'Team',

        # nfldb.version
        '__version__',
    ]

## Index

- #### Module variables

  - __version__
  - api_version
  - stat_categories

- #### Functions

  - QueryOR
  - aggregate
  - connect
  - current
  - now
  - schema_version
  - set_timezone
  - standard_team

- #### Classes

  - Category
    - __init__
  - Clock
    - from_str
    - __init__
  - Drive
    - from_id
    - from_row
    - __init__
  - Enums
  - FieldPosition
    - from_str
    - __init__
    - add_yards
  - Game
    - from_id
    - from_row
    - __init__
  - Play
    - from_id
    - from_row
    - __init__
  - PlayPlayer
    - from_row
    - __init__
  - Player
    - from_id
    - from_row
    - __init__
  - PossessionTime
    - clock
    - __init__
  - Query
    - __init__
    - aggregate
    - andalso
    - as_aggregate
    - as_drives
    - as_games
    - as_play_players
    - as_players
    - as_plays
    - drive
    - game
    - limit
    - play
    - player
    - sort
  - Team
    - __init__
  - Tx
    - __init__

## Module variables

var __version__

The version of the installed nfldb module.

var api_version

The schema version that this library corresponds to. When the schema version of the database is less than this value, `connect` will automatically update the schema to the latest version before doing anything else.

var stat_categories

An ordered dictionary of every statistical category available for play-by-play data. The keys are the category identifier (e.g., `passing_yds`) and the values are `Category` objects.

## Functions

def QueryOR(

db)

Creates a disjunctive `Query` object, where every condition is combined disjunctively. Namely, it is an alias for `nfldb.Query(db, orelse=True)`.

Show source.

    def QueryOR(db):
        """
        Creates a disjunctive `nfldb.Query` object, where every
        condition is combined disjunctively. Namely, it is an alias for
        `nfldb.Query(db, orelse=True)`.
        """
        return Query(db, orelse=True)

def aggregate(

objs)

Given any collection of Python objects that provide a `play_players` attribute, `aggregate` will return a list of `PlayPlayer` objects with statistics aggregated (summed) over each player. (As a special case, if an element in `objs` is itself a `PlayPlayer` object, then it is used and a `play_players` attribute is not rquired.)

For example, `objs` could be a mixed list of `Game` and `Play` objects.

The order of the list returned is stable with respect to the order of players obtained from each element in `objs`.

It is recommended to use `aggregate` and `as_aggregate` instead of this function since summing statistics in the database is much faster. However, this function is provided for aggregation that cannot be expressed by the query interface.

Show source.

    def aggregate(objs):
        """
        Given any collection of Python objects that provide a
        `play_players` attribute, `aggregate` will return a list of
        `PlayPlayer` objects with statistics aggregated (summed) over each
        player. (As a special case, if an element in `objs` is itself a
        `nfldb.PlayPlayer` object, then it is used and a `play_players`
        attribute is not rquired.)

        For example, `objs` could be a mixed list of `nfldb.Game` and
        `nfldb.Play` objects.

        The order of the list returned is stable with respect to the
        order of players obtained from each element in `objs`.

        It is recommended to use `nfldb.Query.aggregate` and
        `nfldb.Query.as_aggregate` instead of this function since summing
        statistics in the database is much faster. However, this function
        is provided for aggregation that cannot be expressed by the query
        interface.
        """
        summed = OrderedDict()
        for obj in objs:
            pps = [obj] if isinstance(obj, types.PlayPlayer) else obj.play_players
            for pp in pps:
                if pp.player_id not in summed:
                    summed[pp.player_id] = pp._copy()
                else:
                    summed[pp.player_id]._add(pp)
        return summed.values()

def connect(

database=None, user=None, password=None, host=None, port=None, timezone=None, config_path='')

Returns a `psycopg2._psycopg.connection` object from the `psycopg2.connect` function. If database is `None`, then `connect` will look for a configuration file using `nfldb.config` with `config_path`. Otherwise, the connection will use the parameters given.

If `database` is `None` and no config file can be found, then an `IOError` exception is raised.

This function will also compare the current schema version of the database against the API version `api_version` and assert that they are equivalent. If the schema library version is less than the the API version, then the schema will be automatically upgraded. If the schema version is newer than the library version, then this function will raise an assertion error. An assertion error will also be raised if the schema version is 0 and the database is not empty.

N.B. The `timezone` parameter should be set to a value that PostgreSQL will accept. Select from the `pg_timezone_names` view to get a list of valid time zones.

Show source.

    def connect(database=None, user=None, password=None, host=None, port=None,
                timezone=None, config_path=''):
        """
        Returns a `psycopg2._psycopg.connection` object from the
        `psycopg2.connect` function. If database is `None`, then `connect`
        will look for a configuration file using `nfldb.config` with
        `config_path`. Otherwise, the connection will use the parameters
        given.

        If `database` is `None` and no config file can be found, then an
        `IOError` exception is raised.

        This function will also compare the current schema version of the
        database against the API version `nfldb.api_version` and assert
        that they are equivalent. If the schema library version is less
        than the the API version, then the schema will be automatically
        upgraded. If the schema version is newer than the library version,
        then this function will raise an assertion error. An assertion
        error will also be raised if the schema version is 0 and the
        database is not empty.

        N.B. The `timezone` parameter should be set to a value that
        PostgreSQL will accept. Select from the `pg_timezone_names` view
        to get a list of valid time zones.
        """
        if database is None:
            conf = config(config_path=config_path)
            if conf is None:
                raise IOError("Could not find valid configuration file.")

            timezone, database = conf['timezone'], conf['database']
            user, password = conf['user'], conf['password']
            host, port = conf['host'], conf['port']

        conn = psycopg2.connect(database=database, user=user, password=password,
                                host=host, port=port,
                                cursor_factory=RealDictCursor)

        # Start the migration. Make sure if this is the initial setup that
        # the DB is empty.
        sversion = schema_version(conn)
        assert sversion <= api_version, \
            'Library with version %d is older than the schema with version %d' \
            % (api_version, sversion)
        assert sversion > 0 or (sversion == 0 and _is_empty(conn)), \
            'Schema has version 0 but is not empty.'
        set_timezone(conn, 'UTC')
        _migrate(conn, api_version)

        if timezone is not None:
            set_timezone(conn, timezone)

        # Bind SQL -> Python casting functions.
        from nfldb.types import Clock, _Enum, Enums, FieldPosition, PossessionTime
        _bind_type(conn, 'game_phase', _Enum._pg_cast(Enums.game_phase))
        _bind_type(conn, 'season_phase', _Enum._pg_cast(Enums.season_phase))
        _bind_type(conn, 'game_day', _Enum._pg_cast(Enums.game_day))
        _bind_type(conn, 'player_pos', _Enum._pg_cast(Enums.player_pos))
        _bind_type(conn, 'player_status', _Enum._pg_cast(Enums.player_status))
        _bind_type(conn, 'game_time', Clock._pg_cast)
        _bind_type(conn, 'pos_period', PossessionTime._pg_cast)
        _bind_type(conn, 'field_pos', FieldPosition._pg_cast)

        return conn

def current(

db)

Returns a triple of `season_phase`, season year and week corresponding to values that `nfldb` thinks are current.

Note that this only queries the database. Only the `nfldb-update` script fetches the current state from NFL.com.

The values retrieved may be `None` if the season is over or if they haven't been updated yet by the `nfldb-update` script.

Show source.

    def current(db):
        """
        Returns a triple of `nfldb.Enums.season_phase`, season year and week
        corresponding to values that `nfldb` thinks are current.

        Note that this only queries the database. Only the `nfldb-update`
        script fetches the current state from NFL.com.

        The values retrieved may be `None` if the season is over or if they
        haven't been updated yet by the `nfldb-update` script.
        """
        with Tx(db, factory=tuple_cursor) as cursor:
            cursor.execute('SELECT season_type, season_year, week FROM meta')
            return cursor.fetchone()
        return tuple([None] * 3)

def now(

)

Returns the current date/time in UTC as a `datetime.datetime` object. It can be used to compare against date/times in any of the `nfldb` objects without worrying about timezones.

Show source.

    def now():
        """
        Returns the current date/time in UTC as a `datetime.datetime`
        object. It can be used to compare against date/times in any of the
        `nfldb` objects without worrying about timezones.
        """
        return datetime.datetime.now(pytz.utc)

def schema_version(

conn)

Returns the schema version of the given database. If the version is not stored in the database, then `0` is returned.

Show source.

    def schema_version(conn):
        """
        Returns the schema version of the given database. If the version
        is not stored in the database, then `0` is returned.
        """
        with Tx(conn) as c:
            try:
                c.execute('SELECT version FROM meta LIMIT 1', ['version'])
            except psycopg2.ProgrammingError:
                conn.rollback()
                return 0
            if c.rowcount == 0:
                return 0
            return c.fetchone()['version']

def set_timezone(

conn, timezone)

Sets the timezone for which all datetimes will be displayed as. Valid values are exactly the same set of values accepted by PostgreSQL. (Select from the `pg_timezone_names` view to get a list of valid time zones.)

Note that all datetimes are stored in UTC. This setting only affects how datetimes are viewed from select queries.

Show source.

    def set_timezone(conn, timezone):
        """
        Sets the timezone for which all datetimes will be displayed
        as. Valid values are exactly the same set of values accepted
        by PostgreSQL. (Select from the `pg_timezone_names` view to
        get a list of valid time zones.)

        Note that all datetimes are stored in UTC. This setting only
        affects how datetimes are viewed from select queries.
        """
        with Tx(conn) as c:
            c.execute('SET timezone = %s', (timezone,))

def standard_team(

team)

Returns a standard abbreviation when team corresponds to a team known by nfldb (case insensitive). If no team can be found, then `"UNK"` is returned.

Show source.

    def standard_team(team):
        """
        Returns a standard abbreviation when team corresponds to a team
        known by nfldb (case insensitive). If no team can be found, then
        `"UNK"` is returned.
        """
        if not team:
            return 'UNK'
        team = team.lower()
        for variants in teams:
            for variant in variants:
                if team == variant.lower():
                    return variants[0]
        return 'UNK'

## Classes

class Category

Represents meta data about a statistical category. This includes the category's scope, GSIS identifier, name and short description.

Show source.

    class Category (object):
        """
        Represents meta data about a statistical category. This includes
        the category's scope, GSIS identifier, name and short description.
        """
        __slots__ = ['category_id', 'gsis_number', 'category_type',
                     'is_real', 'description']

        def __init__(self, category_id, gsis_number, category_type,
                     is_real, description):
            self.category_id = category_id
            """
            A unique name for this category.
            """
            self.gsis_number = gsis_number
            """
            A unique numeric identifier for this category.
            """
            self.category_type = category_type
            """
            The scope of this category represented with
            `nfldb.Enums.category_scope`.
            """
            self.is_real = is_real
            """
            Whether this statistic is a whole number of not. Currently,
            only the `defense_sk` statistic has `Category.is_real` set to
            `True`.
            """
            self.description = description
            """
            A free-form text description of this category.
            """

        @property
        def _sql_field(self):
            """
            The SQL definition of this column. Statistics are always
            NOT NULL and have a default value of `0`.

            When `Category.is_real` is `True`, then the SQL type is `real`.
            Otherwise, it's `smallint`.
            """
            typ = 'real' if self.is_real else 'smallint'
            default = '0.0' if self.is_real else '0'
            return '%s %s NOT NULL DEFAULT %s' % (self.category_id, typ, default)

        def __str__(self):
            return self.category_id

        def __eq__(self, other):
            return self.category_id == other.category_id

#### Ancestors (in MRO)

- Category
- __builtin__.object

#### Instance variables

var category_id

A unique name for this category.

var category_type

The scope of this category represented with `category_scope`.

var description

A free-form text description of this category.

var gsis_number

A unique numeric identifier for this category.

var is_real

Whether this statistic is a whole number of not. Currently, only the `defense_sk` statistic has `Category.is_real` set to `True`.

#### Methods

def __init__(

self, category_id, gsis_number, category_type, is_real, description)

Show source.

    def __init__(self, category_id, gsis_number, category_type,
                 is_real, description):
        self.category_id = category_id
        """
        A unique name for this category.
        """
        self.gsis_number = gsis_number
        """
        A unique numeric identifier for this category.
        """
        self.category_type = category_type
        """
        The scope of this category represented with
        `nfldb.Enums.category_scope`.
        """
        self.is_real = is_real
        """
        Whether this statistic is a whole number of not. Currently,
        only the `defense_sk` statistic has `Category.is_real` set to
        `True`.
        """
        self.description = description
        """
        A free-form text description of this category.
        """

class Clock

Represents a single point in time during a game. This includes the quarter and the game clock time in addition to other phases of the game such as before the game starts, half time, overtime and when the game ends.

Note that the clock time does not uniquely identify a play, since not all plays consume time on the clock. (e.g., A two point conversion.)

This class defines a total ordering on clock times. Namely, c1 < c2 if and only if c2 is closer to the end of the game than c1.

Show source.

    class Clock (object):
        """
        Represents a single point in time during a game. This includes the
        quarter and the game clock time in addition to other phases of the
        game such as before the game starts, half time, overtime and when
        the game ends.

        Note that the clock time does not uniquely identify a play, since
        not all plays consume time on the clock. (e.g., A two point
        conversion.)

        This class defines a total ordering on clock times. Namely, c1 < c2
        if and only if c2 is closer to the end of the game than c1.
        """

        _nonqs = (Enums.game_phase.Pregame, Enums.game_phase.Half,
                  Enums.game_phase.Final)
        """
        The phases of the game that do not have a time component.
        """

        _phase_max = 900
        """
        The maximum number of seconds in a game phase.
        """

        @staticmethod
        def from_str(phase, clock):
            """
            Introduces a new `nfldb.Clock` object given strings of the game
            phase and the clock. `phase` may be one of the values in the
            `nfldb.Enums.game_phase` enumeration. `clock` must be a clock
            string in the format `MM:SS`, e.g., `4:01` corresponds to a
            game phase with 4 minutes and 1 second remaining.
            """
            assert getattr(Enums.game_phase, phase, None) is not None, \
                '"%s" is not a valid game phase. choose one of %s' \
                % (phase, map(str, Enums.game_phase))

            minutes, seconds = map(int, clock.split(':', 1))
            elapsed = Clock._phase_max - ((minutes * 60) + seconds)
            return Clock(Enums.game_phase[phase], int(elapsed))

        @staticmethod
        def _pg_cast(sqlv, cursor):
            """
            Casts a SQL string of the form `(game_phase, elapsed)` to a
            `nfldb.Clock` object.
            """
            phase, elapsed = map(str.strip, sqlv[1:-1].split(','))
            return Clock(Enums.game_phase[phase], int(elapsed))

        def __init__(self, phase, elapsed):
            """
            Introduces a new `nfldb.Clock` object. `phase` should
            be a value from the `nfldb.Enums.game_phase` enumeration
            while `elapsed` should be the number of seconds elapsed in
            the `phase`. Note that `elapsed` is only applicable when
            `phase` is a quarter (including overtime). In all other
            cases, it will be set to `0`.

            `elapsed` should be in the range `[0, 900]` where `900`
            corresponds to the clock time `0:00` and `0` corresponds
            to the clock time `15:00`.
            """
            assert isinstance(phase, Enums.game_phase)
            assert 0 <= elapsed <= Clock._phase_max

            if phase in Clock._nonqs:
                elapsed = 0

            self.phase = phase
            """
            The phase represented by this clock object. It is guaranteed
            to have type `nfldb.Enums.game_phase`.
            """
            self.elapsed = elapsed
            """
            The number of seconds remaining in this clock's phase of the
            game. It is always set to `0` whenever the phase is not a
            quarter in the game.
            """

        @property
        def minutes(self):
            """
            If the clock has a time component, then the number of whole
            minutes **left in this phase** is returned. Otherwise, `0` is
            returned.
            """
            return (Clock._phase_max - self.elapsed) // 60

        @property
        def seconds(self):
            """
            If the clock has a time component, then the number of seconds
            **left in this phase** is returned. Otherwise, `0` is returned.
            """
            return (Clock._phase_max - self.elapsed) % 60

        def __str__(self):
            phase = self.phase
            if phase in Clock._nonqs:
                return phase.name
            else:
                return '%s %02d:%02d' % (phase.name, self.minutes, self.seconds)

        def __lt__(self, o):
            if self.__class__ is not o.__class__:
                return NotImplemented
            return (self.phase, self.elapsed) < (o.phase, o.elapsed)

        def __eq__(self, o):
            if self.__class__ is not o.__class__:
                return NotImplemented
            return self.phase == o.phase and self.elapsed == o.elapsed

        def __conform__(self, proto):
            if proto is ISQLQuote:
                return AsIs("ROW('%s', %d)::game_time"
                            % (self.phase.name, self.elapsed))
            return None

#### Ancestors (in MRO)

- Clock
- __builtin__.object

#### Static methods

def from_str(

phase, clock)

Introduces a new `Clock` object given strings of the game phase and the clock. `phase` may be one of the values in the `game_phase` enumeration. `clock` must be a clock string in the format `MM:SS`, e.g., `4:01` corresponds to a game phase with 4 minutes and 1 second remaining.

Show source.

    @staticmethod
    def from_str(phase, clock):
        """
        Introduces a new `nfldb.Clock` object given strings of the game
        phase and the clock. `phase` may be one of the values in the
        `nfldb.Enums.game_phase` enumeration. `clock` must be a clock
        string in the format `MM:SS`, e.g., `4:01` corresponds to a
        game phase with 4 minutes and 1 second remaining.
        """
        assert getattr(Enums.game_phase, phase, None) is not None, \
            '"%s" is not a valid game phase. choose one of %s' \
            % (phase, map(str, Enums.game_phase))
        minutes, seconds = map(int, clock.split(':', 1))
        elapsed = Clock._phase_max - ((minutes * 60) + seconds)
        return Clock(Enums.game_phase[phase], int(elapsed))

#### Instance variables

var elapsed

The number of seconds remaining in this clock's phase of the game. It is always set to `0` whenever the phase is not a quarter in the game.

var minutes

If the clock has a time component, then the number of whole minutes left in this phase is returned. Otherwise, `0` is returned.

var phase

The phase represented by this clock object. It is guaranteed to have type `game_phase`.

var seconds

If the clock has a time component, then the number of seconds left in this phase is returned. Otherwise, `0` is returned.

#### Methods

def __init__(

self, phase, elapsed)

Introduces a new `Clock` object. `phase` should be a value from the `game_phase` enumeration while `elapsed` should be the number of seconds elapsed in the `phase`. Note that `elapsed` is only applicable when `phase` is a quarter (including overtime). In all other cases, it will be set to `0`.

`elapsed` should be in the range `[0, 900]` where `900` corresponds to the clock time `0:00` and `0` corresponds to the clock time `15:00`.

Show source.

    def __init__(self, phase, elapsed):
        """
        Introduces a new `nfldb.Clock` object. `phase` should
        be a value from the `nfldb.Enums.game_phase` enumeration
        while `elapsed` should be the number of seconds elapsed in
        the `phase`. Note that `elapsed` is only applicable when
        `phase` is a quarter (including overtime). In all other
        cases, it will be set to `0`.
        `elapsed` should be in the range `[0, 900]` where `900`
        corresponds to the clock time `0:00` and `0` corresponds
        to the clock time `15:00`.
        """
        assert isinstance(phase, Enums.game_phase)
        assert 0 <= elapsed <= Clock._phase_max
        if phase in Clock._nonqs:
            elapsed = 0
        self.phase = phase
        """
        The phase represented by this clock object. It is guaranteed
        to have type `nfldb.Enums.game_phase`.
        """
        self.elapsed = elapsed
        """
        The number of seconds remaining in this clock's phase of the
        game. It is always set to `0` whenever the phase is not a
        quarter in the game.
        """

class Drive

Represents a single drive in an NFL game. Each drive has an assortment of meta data, possibly including the start and end times, the start and end field positions, the result of the drive, the number of penalties and first downs, and more.

Each drive corresponds to zero or more plays. A drive usually corresponds to at least one play, but if the game is active, there exist valid ephemeral states where a drive has no plays.

Show source.

    class Drive (object):
        """
        Represents a single drive in an NFL game. Each drive has an
        assortment of meta data, possibly including the start and end
        times, the start and end field positions, the result of the drive,
        the number of penalties and first downs, and more.

        Each drive corresponds to **zero or more** plays. A drive usually
        corresponds to at least one play, but if the game is active, there
        exist valid ephemeral states where a drive has no plays.
        """
        _table = 'drive'

        _sql_columns = ['gsis_id', 'drive_id', 'start_field', 'start_time',
                        'end_field', 'end_time', 'pos_team', 'pos_time',
                        'first_downs', 'result', 'penalty_yards', 'yards_gained',
                        'play_count',
                        'time_inserted', 'time_updated',
                        ]

        _sql_derived = []

        _sql_fields = _sql_columns + _sql_derived

        __slots__ = _sql_fields + ['_db', '_game', '_plays']

        @staticmethod
        def _as_sql(field, prefix=None):
            prefix = 'drive.' if prefix is None else prefix
            if field in Drive._sql_columns:
                return '%s%s' % (prefix, field)
            raise AttributeError(field)

        @staticmethod
        def _from_nflgame(db, g, d):
            """
            Given `g` as a `nfldb.Game` object and `d` as a
            `nflgame.game.Drive` object, `_from_nflgame` converts `d` to a
            `nfldb.Drive` object.

            Generally, this function should not be used. It is called
            automatically by `nfldb.Game._from_nflgame`.
            """
            start_time = _nflgame_clock(d.time_start)
            start_field = FieldPosition(getattr(d.field_start, 'offset', None))
            end_field = FieldPosition(d.field_end.offset)
            end_time = _nflgame_clock(d.time_end)
            team = nfldb.team.standard_team(d.team)
            drive = Drive(db, g.gsis_id, d.drive_num, start_field, start_time,
                          end_field, end_time, team,
                          PossessionTime(d.pos_time.total_seconds()),
                          d.first_downs, d.result, d.penalty_yds,
                          d.total_yds, d.play_cnt, None, None)

            drive._game = g
            candidates = []
            for play in d.plays:
                candidates.append(Play._from_nflgame(db, drive, play))

            # At this point, some plays don't have valid game times. Fix it!
            # If we absolutely cannot fix it, drop the play. Maintain integrity!
            drive._plays = []
            for play in candidates:
                if play.time is None:
                    play.time = _play_time(drive, play,
                                           _next_play_with_time(candidates, play))
                if play.time is not None:
                    drive._plays.append(play)
            return drive

        @staticmethod
        def from_row(db, r):
            """
            Introduces a new `nfldb.Drive` object from a full SQL row
            result from the `drive` table.
            """
            return Drive(db, r['gsis_id'], r['drive_id'], r['start_field'],
                         r['start_time'], r['end_field'], r['end_time'],
                         r['pos_team'], r['pos_time'], r['first_downs'],
                         r['result'], r['penalty_yards'], r['yards_gained'],
                         r['play_count'], r['time_inserted'], r['time_updated'])

        @staticmethod
        def from_id(db, gsis_id, drive_id):
            """
            Given a GSIS identifier (e.g., `2012090500`) as a string
            and a integer drive id, this returns a `nfldb.Drive` object
            corresponding to the given identifiers.

            If no corresponding drive is found, then `None` is returned.
            """
            with Tx(db) as cursor:
                cursor.execute('''
                    SELECT %s FROM drive WHERE (gsis_id, drive_id) = %s
                ''' % (_select_fields(Drive), '%s'), ((gsis_id, drive_id),))
                if cursor.rowcount > 0:
                    return Drive.from_row(db, cursor.fetchone())
            return None

        def __init__(self, db, gsis_id, drive_id, start_field, start_time,
                     end_field, end_time, pos_team, pos_time,
                     first_downs, result, penalty_yards, yards_gained, play_count,
                     time_inserted, time_updated):
            """
            Introduces a new `nfldb.Drive` object with the given attributes.

            This constructor should probably not be used. Instead, use
            `nfldb.Drive.from_row` or `nfldb.Drive.from_id`. Alternatively,
            the `nfldb.Game`.`nfldb.Game.drives` attribute contains all
            drives in the corresponding game.
            """
            self._game = None
            self._plays = None
            self._db = db

            self.gsis_id = gsis_id
            """
            The GSIS identifier for the game that this drive belongs to.
            """
            self.drive_id = drive_id
            """
            The numeric drive identifier for this drive. It may be
            interpreted as a sequence number.
            """
            self.start_field = start_field
            """
            The starting field position of this drive represented
            with `nfldb.FieldPosition`.
            """
            self.start_time = start_time
            """
            The starting clock time of this drive, represented with
            `nfldb.Clock`.
            """
            self.end_field = end_field
            """
            The ending field position of this drive represented with
            `nfldb.FieldPosition`.
            """
            self.end_time = end_time
            """
            The ending clock time of this drive, represented with
            `nfldb.Clock`.
            """
            self.pos_team = pos_team
            """
            The team in possession during this drive, represented as
            a team abbreviation string. Use the `nfldb.Team` constructor
            to get more information on a team.
            """
            self.pos_time = pos_time
            """
            The possession time of this drive, represented with
            `nfldb.PossessionTime`.
            """
            self.first_downs = first_downs
            """
            The number of first downs that occurred in this drive.
            """
            self.result = result
            """
            A freeform text field straight from NFL's GameCenter data that
            sometimes contains the result of a drive (e.g., `Touchdown`).
            """
            self.penalty_yards = penalty_yards
            """
            The number of yards lost or gained from penalties in this
            drive.
            """
            self.yards_gained = yards_gained
            """
            The total number of yards gained or lost in this drive.
            """
            self.play_count = play_count
            """
            The total number of plays executed by the offense in this
            drive.
            """
            self.time_inserted = time_inserted
            """The date and time that this drive was added."""
            self.time_updated = time_updated
            """The date and time that this drive was last updated."""

        @property
        def game(self):
            """
            Returns the `nfldb.Game` object that contains this drive. The
            game is retrieved from the database if it hasn't been already.
            """
            if self._game is None:
                return Game.from_id(self.gsis_id)
            return self._game

        @property
        def plays(self):
            """
            A list of all `nfldb.Play`s in this drive. They are
            automatically retrieved from the database if they haven't been
            already.

            If there are no plays in the drive, then an empty list is
            returned.
            """
            if self._plays is None:
                self._plays = []
                with Tx(self._db) as cursor:
                    q = '''
                        SELECT %s FROM play WHERE (gsis_id, drive_id) = %s
                    ''' % (_select_fields(Play), '%s')
                    cursor.execute(q, ((self.gsis_id, self.drive_id),))
                    for row in cursor.fetchall():
                        p = Play.from_row(self._db, row)
                        p._drive = self
                        self._plays.append(p)
            return self._plays

        @property
        def play_players(self):
            """
            A list of `nfldb.PlayPlayer` objects in this drive. Data is
            retrieved from the database if it hasn't been already.
            """
            pps = []
            for play in self.plays:
                for pp in play.play_players:
                    pps.append(pp)
            return pps

        @property
        def _row(self):
            return _as_row(Drive._sql_columns, self)

        def _save(self, cursor):
            vals = self._row
            _upsert(cursor, 'drive', vals, vals[0:2])

            # Remove any plays that are stale.
            cursor.execute('''
                DELETE FROM play
                WHERE gsis_id = %s AND drive_id = %s AND NOT (play_id = ANY (%s))
            ''', (self.gsis_id, self.drive_id, [p.play_id for p in self._plays]))
            for play in (self._plays or []):
                play._save(cursor)

        def __str__(self):
            s = '[%-12s] %-3s from %-6s to %-6s '
            s += '(lasted %s - %s to %s)'
            return s % (
                self.result, self.pos_team, self.start_field, self.end_field,
                self.pos_time, self.start_time, self.end_time,
            )

#### Ancestors (in MRO)

- Drive
- __builtin__.object

#### Static methods

def from_id(

db, gsis_id, drive_id)

Given a GSIS identifier (e.g., `2012090500`) as a string and a integer drive id, this returns a `Drive` object corresponding to the given identifiers.

If no corresponding drive is found, then `None` is returned.

Show source.

    @staticmethod
    def from_id(db, gsis_id, drive_id):
        """
        Given a GSIS identifier (e.g., `2012090500`) as a string
        and a integer drive id, this returns a `nfldb.Drive` object
        corresponding to the given identifiers.
        If no corresponding drive is found, then `None` is returned.
        """
        with Tx(db) as cursor:
            cursor.execute('''
                SELECT %s FROM drive WHERE (gsis_id, drive_id) = %s
            ''' % (_select_fields(Drive), '%s'), ((gsis_id, drive_id),))
            if cursor.rowcount > 0:
                return Drive.from_row(db, cursor.fetchone())
        return None

def from_row(

db, r)

Introduces a new `Drive` object from a full SQL row result from the `drive` table.

Show source.

    @staticmethod
    def from_row(db, r):
        """
        Introduces a new `nfldb.Drive` object from a full SQL row
        result from the `drive` table.
        """
        return Drive(db, r['gsis_id'], r['drive_id'], r['start_field'],
                     r['start_time'], r['end_field'], r['end_time'],
                     r['pos_team'], r['pos_time'], r['first_downs'],
                     r['result'], r['penalty_yards'], r['yards_gained'],
                     r['play_count'], r['time_inserted'], r['time_updated'])

#### Instance variables

var drive_id

The numeric drive identifier for this drive. It may be interpreted as a sequence number.

var end_field

The ending field position of this drive represented with `FieldPosition`.

var end_time

The ending clock time of this drive, represented with `Clock`.

var first_downs

The number of first downs that occurred in this drive.

var game

Returns the `Game` object that contains this drive. The game is retrieved from the database if it hasn't been already.

var gsis_id

The GSIS identifier for the game that this drive belongs to.

var penalty_yards

The number of yards lost or gained from penalties in this drive.

var play_count

The total number of plays executed by the offense in this drive.

var play_players

A list of `PlayPlayer` objects in this drive. Data is retrieved from the database if it hasn't been already.

var plays

A list of all `Play`s in this drive. They are automatically retrieved from the database if they haven't been already.

If there are no plays in the drive, then an empty list is returned.

var pos_team

The team in possession during this drive, represented as a team abbreviation string. Use the `Team` constructor to get more information on a team.

var pos_time

The possession time of this drive, represented with `PossessionTime`.

var result

A freeform text field straight from NFL's GameCenter data that sometimes contains the result of a drive (e.g., `Touchdown`).

var start_field

The starting field position of this drive represented with `FieldPosition`.

var start_time

The starting clock time of this drive, represented with `Clock`.

var time_inserted

The date and time that this drive was added.

var time_updated

The date and time that this drive was last updated.

var yards_gained

The total number of yards gained or lost in this drive.

#### Methods

def __init__(

self, db, gsis_id, drive_id, start_field, start_time, end_field, end_time, pos_team, pos_time, first_downs, result, penalty_yards, yards_gained, play_count, time_inserted, time_updated)

Introduces a new `Drive` object with the given attributes.

This constructor should probably not be used. Instead, use `from_row` or `from_id`. Alternatively, the `Game`.`drives` attribute contains all drives in the corresponding game.

Show source.

    def __init__(self, db, gsis_id, drive_id, start_field, start_time,
                 end_field, end_time, pos_team, pos_time,
                 first_downs, result, penalty_yards, yards_gained, play_count,
                 time_inserted, time_updated):
        """
        Introduces a new `nfldb.Drive` object with the given attributes.
        This constructor should probably not be used. Instead, use
        `nfldb.Drive.from_row` or `nfldb.Drive.from_id`. Alternatively,
        the `nfldb.Game`.`nfldb.Game.drives` attribute contains all
        drives in the corresponding game.
        """
        self._game = None
        self._plays = None
        self._db = db
        self.gsis_id = gsis_id
        """
        The GSIS identifier for the game that this drive belongs to.
        """
        self.drive_id = drive_id
        """
        The numeric drive identifier for this drive. It may be
        interpreted as a sequence number.
        """
        self.start_field = start_field
        """
        The starting field position of this drive represented
        with `nfldb.FieldPosition`.
        """
        self.start_time = start_time
        """
        The starting clock time of this drive, represented with
        `nfldb.Clock`.
        """
        self.end_field = end_field
        """
        The ending field position of this drive represented with
        `nfldb.FieldPosition`.
        """
        self.end_time = end_time
        """
        The ending clock time of this drive, represented with
        `nfldb.Clock`.
        """
        self.pos_team = pos_team
        """
        The team in possession during this drive, represented as
        a team abbreviation string. Use the `nfldb.Team` constructor
        to get more information on a team.
        """
        self.pos_time = pos_time
        """
        The possession time of this drive, represented with
        `nfldb.PossessionTime`.
        """
        self.first_downs = first_downs
        """
        The number of first downs that occurred in this drive.
        """
        self.result = result
        """
        A freeform text field straight from NFL's GameCenter data that
        sometimes contains the result of a drive (e.g., `Touchdown`).
        """
        self.penalty_yards = penalty_yards
        """
        The number of yards lost or gained from penalties in this
        drive.
        """
        self.yards_gained = yards_gained
        """
        The total number of yards gained or lost in this drive.
        """
        self.play_count = play_count
        """
        The total number of plays executed by the offense in this
        drive.
        """
        self.time_inserted = time_inserted
        """The date and time that this drive was added."""
        self.time_updated = time_updated
        """The date and time that this drive was last updated."""

class Enums

Enums groups all enum types used in the database schema. All possible values for each enum type are represented as lists. The ordering of each list is the same as the ordering in the database. In particular, this ordering specifies a total ordering that can be used in Python code to compare values in the same enumeration.

Show source.

    class Enums (object):
        """
        Enums groups all enum types used in the database schema.
        All possible values for each enum type are represented as lists.
        The ordering of each list is the same as the ordering in the
        database. In particular, this ordering specifies a total ordering
        that can be used in Python code to compare values in the same
        enumeration.
        """

        game_phase = _Enum('game_phase',
                           ['Pregame', 'Q1', 'Q2', 'Half',
                            'Q3', 'Q4', 'OT', 'OT2', 'Final'])
        """
        Represents the phase of the game. e.g., `Q1` or `HALF`.
        """

        season_phase = _Enum('season_phase',
                             ['Preseason', 'Regular', 'Postseason'])
        """
        Represents one of the three phases of an NFL season: `Preseason`,
        `Regular` or `Postseason`.
        """

        game_day = _Enum('game_day',
                         ['Sunday', 'Monday', 'Tuesday', 'Wednesday',
                          'Thursday', 'Friday', 'Saturday'])
        """
        The day of the week on which a game was played. The week starts
        on `Sunday`.
        """

        player_pos = _Enum('player_pos',
                           ['C', 'CB', 'DB', 'DE', 'DL', 'DT', 'FB', 'FS', 'G',
                            'ILB', 'K', 'LB', 'LS', 'MLB', 'NT', 'OG', 'OL', 'OLB',
                            'OT', 'P', 'QB', 'RB', 'SAF', 'SS', 'T', 'TE', 'WR',
                            'UNK'])
        """
        The set of all possible player positions in abbreviated form.
        """

        player_status = _Enum('player_status',
                              ['Active', 'InjuredReserve', 'NonFootballInjury',
                               'Suspended', 'PUP', 'UnsignedDraftPick',
                               'Exempt', 'Unknown'])
        """
        The current status of a player that is actively on a
        roster. The statuses are taken from the key at the bottom of
        http://goo.gl/HHsnjD
        """

        category_scope = _Enum('category_scope', ['play', 'player'])
        """
        The scope of a particular statistic. Typically, statistics refer
        to a specific `player`, but sometimes a statistic refers to the
        totality of a play. For example, `third_down_att` is a `play`
        statistic that records third down attempts.

        Currently, `play` and `player` are the only possible values.

        Note that this type is not represented in the database schema.
        Values of this type are constructed from data in `category.py`.
        """

        _nflgame_season_phase = {
            'PRE': season_phase.Preseason,
            'REG': season_phase.Regular,
            'POST': season_phase.Postseason,
        }
        """
        Maps a season type in `nflgame` to a `nfldb.Enums.season_phase`.
        """

        _nflgame_game_phase = {
            'Pregame': game_phase.Pregame,
            'Halftime': game_phase.Half,
            'Final': game_phase.Final,
            'final': game_phase.Final,
            1: game_phase.Q1,
            2: game_phase.Q2,
            3: game_phase.Half,
            4: game_phase.Q3,
            5: game_phase.Q4,
            6: game_phase.OT,
            7: game_phase.OT2,
        }
        """
        Maps a game phase in `nflgame` to a `nfldb.Enums.game_phase`.
        """

        _nflgame_game_day = {
            'Sun': game_day.Sunday,
            'Mon': game_day.Monday,
            'Tue': game_day.Tuesday,
            'Wed': game_day.Wednesday,
            'Thu': game_day.Thursday,
            'Fri': game_day.Friday,
            'Sat': game_day.Saturday,
        }
        """
        Maps a game day of the week in `nflgame` to a
        `nfldb.Enums.game_day`.
        """

        _nflgame_player_status = {
            'ACT': player_status.Active,
            'RES': player_status.InjuredReserve,
            'NON': player_status.NonFootballInjury,
            'Suspended': player_status.Suspended,
            'PUP': player_status.PUP,
            'UDF': player_status.UnsignedDraftPick,
            'EXE': player_status.Exempt,
            # Everything else is `player_status.Unknown`
        }

#### Ancestors (in MRO)

- Enums
- __builtin__.object

#### Class variables

var category_scope

The scope of a particular statistic. Typically, statistics refer to a specific `player`, but sometimes a statistic refers to the totality of a play. For example, `third_down_att` is a `play` statistic that records third down attempts.

Currently, `play` and `player` are the only possible values.

Note that this type is not represented in the database schema. Values of this type are constructed from data in `category.py`.

var game_day

The day of the week on which a game was played. The week starts on `Sunday`.

var game_phase

Represents the phase of the game. e.g., `Q1` or `HALF`.

var player_pos

The set of all possible player positions in abbreviated form.

var player_status

The current status of a player that is actively on a roster. The statuses are taken from the key at the bottom of http://goo.gl/HHsnjD

var season_phase

Represents one of the three phases of an NFL season: `Preseason`, `Regular` or `Postseason`.

class FieldPosition

Represents field position.

The representation is an integer offset where the 50 yard line corresponds to '0'. Being in one's own territory corresponds to a negative offset while being in the opponent's territory corresponds to a positive offset.

e.g., NE has the ball on the NE 45, the offset is -5. e.g., NE has the ball on the NYG 2, the offset is 48.

This class also defines a total ordering on field positions. Namely, given f1 and f2, f1 < f2 if and only if f2 is closer to the goal line for the team with possession of the football.

Show source.

    class FieldPosition (object):
        """
        Represents field position.

        The representation is an integer offset where the 50 yard line
        corresponds to '0'. Being in one's own territory corresponds to a
        negative offset while being in the opponent's territory corresponds
        to a positive offset.

        e.g., NE has the ball on the NE 45, the offset is -5.
        e.g., NE has the ball on the NYG 2, the offset is 48.

        This class also defines a total ordering on field
        positions. Namely, given f1 and f2, f1 < f2 if and only if f2
        is closer to the goal line for the team with possession of the
        football.
        """
        __slots__ = ['_offset']

        @staticmethod
        def _pg_cast(sqlv, cursor):
            if not sqlv:
                return FieldPosition(None)
            return FieldPosition(int(sqlv[1:-1]))

        @staticmethod
        def from_str(pos):
            """
            Given a string `pos` in the format `FIELD YARDLINE`, this
            returns a new `FieldPosition` object representing the yardline
            given. `FIELD` must be the string `OWN` or `OPP` and `YARDLINE`
            must be an integer in the range `[0, 50]`.

            For example, `OPP 19` corresponds to an offset of `31`
            and `OWN 5` corresponds to an offset of `-45`. Midfield can be
            expressed as either `OWN 0` or `OPP 0`.
            """
            field, yrdline = pos.split(' ')
            field, yrdline = field.upper(), int(yrdline)
            assert field in ('OWN', 'OPP')
            assert 0 <= yrdline <= 50

            if field == 'OWN':
                return FieldPosition(yrdline - 50)
            else:
                return FieldPosition(50 - yrdline)

        def __init__(self, offset):
            """
            Makes a new `nfldb.FieldPosition` given a field `offset`.
            `offset` must be in the integer range [-50, 50].
            """
            if offset is None:
                self._offset = None
                return
            assert -50 <= offset <= 50
            self._offset = offset

        def add_yards(self, yards):
            """
            Returns a new `nfldb.FieldPosition` with `yards` added to this
            field position. The value of `yards` may be negative.
            """
            assert self.valid
            newoffset = max(-50, min(50, self._offset + yards))
            return FieldPosition(newoffset)

        @property
        def valid(self):
            """
            Returns `True` if and only if this field position is known and
            valid.

            Invalid field positions cannot be compared with other field
            positions.
            """
            return self._offset is not None

        def __lt__(self, other):
            if self.__class__ is not other.__class__:
                return NotImplemented
            assert self.valid and other.valid
            return self._offset < other._offset

        def __eq__(self, other):
            if self.__class__ is not other.__class__:
                return NotImplemented
            return self._offset == other._offset

        def __str__(self):
            if not self.valid:
                return 'N/A'
            elif self._offset > 0:
                return 'OPP %d' % (50 - self._offset)
            elif self._offset < 0:
                return 'OWN %d' % (50 + self._offset)
            else:
                return 'MIDFIELD'

        def __conform__(self, proto):
            if proto is ISQLQuote:
                if not self.valid:
                    return AsIs("NULL")
                else:
                    return AsIs("ROW(%d)::field_pos" % self._offset)
            return None

#### Ancestors (in MRO)

- FieldPosition
- __builtin__.object

#### Static methods

def from_str(

pos)

Given a string `pos` in the format `FIELD YARDLINE`, this returns a new `FieldPosition` object representing the yardline given. `FIELD` must be the string `OWN` or `OPP` and `YARDLINE` must be an integer in the range `[0, 50]`.

For example, `OPP 19` corresponds to an offset of `31` and `OWN 5` corresponds to an offset of `-45`. Midfield can be expressed as either `OWN 0` or `OPP 0`.

Show source.

    @staticmethod
    def from_str(pos):
        """
        Given a string `pos` in the format `FIELD YARDLINE`, this
        returns a new `FieldPosition` object representing the yardline
        given. `FIELD` must be the string `OWN` or `OPP` and `YARDLINE`
        must be an integer in the range `[0, 50]`.
        For example, `OPP 19` corresponds to an offset of `31`
        and `OWN 5` corresponds to an offset of `-45`. Midfield can be
        expressed as either `OWN 0` or `OPP 0`.
        """
        field, yrdline = pos.split(' ')
        field, yrdline = field.upper(), int(yrdline)
        assert field in ('OWN', 'OPP')
        assert 0 <= yrdline <= 50
        if field == 'OWN':
            return FieldPosition(yrdline - 50)
        else:
            return FieldPosition(50 - yrdline)

#### Instance variables

var valid

Returns `True` if and only if this field position is known and valid.

Invalid field positions cannot be compared with other field positions.

#### Methods

def __init__(

self, offset)

Makes a new `FieldPosition` given a field `offset`. `offset` must be in the integer range [-50, 50].

Show source.

    def __init__(self, offset):
        """
        Makes a new `nfldb.FieldPosition` given a field `offset`.
        `offset` must be in the integer range [-50, 50].
        """
        if offset is None:
            self._offset = None
            return
        assert -50 <= offset <= 50
        self._offset = offset

def add_yards(

self, yards)

Returns a new `FieldPosition` with `yards` added to this field position. The value of `yards` may be negative.

Show source.

    def add_yards(self, yards):
        """
        Returns a new `nfldb.FieldPosition` with `yards` added to this
        field position. The value of `yards` may be negative.
        """
        assert self.valid
        newoffset = max(-50, min(50, self._offset + yards))
        return FieldPosition(newoffset)

class Game

Represents a single NFL game in the preseason, regular season or post season. Each game has an assortment of meta data, including a quarterly breakdown of scores, turnovers, the time the game started, the season week the game occurred in, and more.

Each game corresponds to zero or more drive. A game usually corresponds to at least one drive, but if the game is active, there exist valid ephemeral states where a game has no drives.

Show source.

    class Game (object):
        """
        Represents a single NFL game in the preseason, regular season or
        post season. Each game has an assortment of meta data, including
        a quarterly breakdown of scores, turnovers, the time the game
        started, the season week the game occurred in, and more.

        Each game corresponds to **zero or more** drive. A game usually
        corresponds to at least one drive, but if the game is active, there
        exist valid ephemeral states where a game has no drives.
        """
        _table = 'game'

        _sql_columns = ['gsis_id', 'gamekey', 'start_time', 'week', 'day_of_week',
                        'season_year', 'season_type', 'finished',
                        'home_team', 'home_score', 'home_score_q1',
                        'home_score_q2', 'home_score_q3', 'home_score_q4',
                        'home_score_q5', 'home_turnovers',
                        'away_team', 'away_score', 'away_score_q1',
                        'away_score_q2', 'away_score_q3', 'away_score_q4',
                        'away_score_q5', 'away_turnovers',
                        'time_inserted', 'time_updated']

        _sql_derived = ['winner', 'loser']

        _sql_fields = _sql_columns + _sql_derived

        __slots__ = _sql_fields + ['_db', '_drives']

        # Document instance variables for derived SQL fields.
        __pdoc__['Game.winner'] = '''The winner of this game.'''
        __pdoc__['Game.loser'] = '''The loser of this game.'''

        @staticmethod
        def _as_sql(field, prefix=None):
            prefix = 'game.' if prefix is None else prefix
            if field in Game._sql_columns:
                return '%s%s' % (prefix, field)
            elif field == 'winner':
                return '''
                    (CASE WHEN {prefix}home_score > {prefix}away_score
                        THEN {prefix}home_team
                        ELSE {prefix}away_team
                     END)'''.format(prefix=prefix)
            elif field == 'loser':
                return '''
                    (CASE WHEN {prefix}home_score < {prefix}away_score
                        THEN {prefix}home_team
                        ELSE {prefix}away_team
                     END)'''.format(prefix=prefix)
            raise AttributeError(field)

        @staticmethod
        def _from_nflgame(db, g):
            """
            Converts a `nflgame.game.Game` object to a `nfldb.Game`
            object.

            `db` should be a psycopg2 connection returned by
            `nfldb.connect`.
            """
            home_team = nfldb.team.standard_team(g.home)
            away_team = nfldb.team.standard_team(g.away)
            season_type = Enums._nflgame_season_phase[g.schedule['season_type']]
            day_of_week = Enums._nflgame_game_day[g.schedule['wday']]
            start_time = _nflgame_start_time(g.schedule)
            finished = g.game_over()

            # If it's been 8 hours since game start, we always conclude finished!
            if (now() - start_time).total_seconds() >= (60 * 60 * 8):
                finished = True

            # The season year should always be the same for every game in the
            # season. e.g., games played in jan-feb of 2013 are in season 2012.
            season_year = g.schedule['year']
            if int(g.eid[4:6]) <= 3:
                season_year -= 1
            game = Game(db, g.eid, g.gamekey, start_time, g.schedule['week'],
                        day_of_week, season_year, season_type, finished,
                        home_team, g.score_home, g.score_home_q1,
                        g.score_home_q2, g.score_home_q3, g.score_home_q4,
                        g.score_home_q5, int(g.data['home']['to']),
                        away_team, g.score_away, g.score_away_q1,
                        g.score_away_q2, g.score_away_q3, g.score_away_q4,
                        g.score_away_q5, int(g.data['away']['to']),
                        None, None)

            game._drives = []
            for drive in g.drives:
                game._drives.append(Drive._from_nflgame(db, game, drive))
            return game

        @staticmethod
        def _from_schedule(db, s):
            """
            Converts a schedule dictionary from the `nflgame.schedule`
            module to a bare-bones `nfldb.Game` object.
            """
            # This is about as evil as it gets. Duck typing to the MAX!
            class _Game (object):
                def __init__(self):
                    self.schedule = s
                    self.home, self.away = s['home'], s['away']
                    self.eid = s['eid']
                    self.gamekey = s['gamekey']
                    self.drives = []
                    self.game_over = lambda: False

                    zeroes = ['score_%s', 'score_%s_q1', 'score_%s_q2',
                              'score_%s_q3', 'score_%s_q4', 'score_%s_q5']
                    for which, k in itertools.product(('home', 'away'), zeroes):
                        setattr(self, k % which, 0)
                    self.data = {'home': {'to': 0}, 'away': {'to': 0}}
            return Game._from_nflgame(db, _Game())

        @staticmethod
        def from_row(db, row):
            """
            Introduces a new `nfldb.Game` object from a full SQL row
            result from the `game` table.
            """
            return Game(db, **row)

        @staticmethod
        def from_id(db, gsis_id):
            """
            Given a GSIS identifier (e.g., `2012090500`) as a string,
            returns a `nfldb.Game` object corresponding to `gsis_id`.

            If no corresponding game is found, `None` is returned.
            """
            with Tx(db) as cursor:
                cursor.execute('''
                    SELECT %s FROM game WHERE gsis_id = %s
                ''' % (_select_fields(Game), '%s'), (gsis_id,))
                if cursor.rowcount > 0:
                    return Game.from_row(db, cursor.fetchone())
            return None

        def __init__(self, db, gsis_id, gamekey, start_time, week, day_of_week,
                     season_year, season_type, finished,
                     home_team, home_score, home_score_q1, home_score_q2,
                     home_score_q3, home_score_q4, home_score_q5, home_turnovers,
                     away_team, away_score, away_score_q1, away_score_q2,
                     away_score_q3, away_score_q4, away_score_q5, away_turnovers,
                     time_inserted, time_updated, loser=None, winner=None):
            """
            Introduces a new `nfldb.Drive` object with the given attributes.

            This constructor should probably not be used. Instead, use
            `nfldb.Game.from_row` or `nfldb.Game.from_id`.
            """
            self._drives = None

            self._db = db
            """
            The psycopg2 database connection.
            """
            self.gsis_id = gsis_id
            """
            The NFL GameCenter id of the game. It is a string
            with 10 characters. The first 8 correspond to the date of the
            game, while the last 2 correspond to an id unique to the week that
            the game was played.
            """
            self.gamekey = gamekey
            """
            Another unique identifier for a game used by the
            NFL. It is a sequence number represented as a 5 character string.
            The gamekey is specifically used to tie games to other resources,
            like the NFL's content delivery network.
            """
            self.start_time = start_time
            """
            A Python datetime object corresponding to the start time of
            the game. The timezone of this value will be equivalent to the
            timezone specified by `nfldb.set_timezone` (which is by default
            set to the value specified in the configuration file).
            """
            self.week = week
            """
            The week number of this game. It is always relative
            to the phase of the season. Namely, the first week of preseason
            is 1 and so is the first week of the regular season.
            """
            self.day_of_week = day_of_week
            """
            The day of the week this game was played on.
            Possible values correspond to the `nfldb.Enums.game_day` enum.
            """
            self.season_year = season_year
            """
            The year of the season of this game. This
            does not necessarily match the year that the game was played. For
            example, games played in January 2013 are in season 2012.
            """
            self.season_type = season_type
            """
            The phase of the season. e.g., `Preseason`,
            `Regular season` or `Postseason`. All valid values correspond
            to the `nfldb.Enums.season_phase`.
            """
            self.finished = finished
            """
            A boolean that is `True` if and only if the game has finished.
            """
            self.home_team = home_team
            """
            The team abbreviation for the home team. Use the `nfldb.Team`
            constructor to get more information on a team.
            """
            self.home_score = home_score
            """The current total score for the home team."""
            self.home_score_q1 = home_score_q1
            """The 1st quarter score for the home team."""
            self.home_score_q2 = home_score_q2
            """The 2nd quarter score for the home team."""
            self.home_score_q3 = home_score_q3
            """The 3rd quarter score for the home team."""
            self.home_score_q4 = home_score_q4
            """The 4th quarter score for the home team."""
            self.home_score_q5 = home_score_q5
            """The OT quarter score for the home team."""
            self.home_turnovers = home_turnovers
            """Total turnovers for the home team."""
            self.away_team = away_team
            """
            The team abbreviation for the away team. Use the `nfldb.Team`
            constructor to get more information on a team.
            """
            self.away_score = away_score
            """The current total score for the away team."""
            self.away_score_q1 = away_score_q1
            """The 1st quarter score for the away team."""
            self.away_score_q2 = away_score_q2
            """The 2nd quarter score for the away team."""
            self.away_score_q3 = away_score_q3
            """The 3rd quarter score for the away team."""
            self.away_score_q4 = away_score_q4
            """The 4th quarter score for the away team."""
            self.away_score_q5 = away_score_q5
            """The OT quarter score for the away team."""
            self.away_turnovers = away_turnovers
            """Total turnovers for the away team."""
            self.time_inserted = time_inserted
            """The date and time that this game was added."""
            self.time_updated = time_updated
            """The date and time that this game was last updated."""

        @property
        def drives(self):
            """
            A list of `nfldb.Drive`s for this game. They are automatically
            loaded from the database if they haven't been already.

            If there are no drives found in the game, then an empty list
            is returned.
            """
            if self._drives is None:
                self._drives = []
                with Tx(self._db) as cursor:
                    cursor.execute('''
                        SELECT %s FROM drive WHERE gsis_id = %s
                    ''' % (_select_fields(Drive), '%s'), (self.gsis_id,))
                    for row in cursor.fetchall():
                        d = Drive.from_row(self._db, row)
                        d._game = self
                        self._drives.append(d)
            return self._drives

        @property
        def plays(self):
            """
            A list of `nfldb.Play` objects in this game. Data is retrieved
            from the database if it hasn't been already.
            """
            plays = []
            for drive in self.drives:
                for play in self.plays:
                    plays.append(play)
            return plays

        @property
        def play_players(self):
            """
            A list of `nfldb.PlayPlayer` objects in this game. Data is
            retrieved from the database if it hasn't been already.
            """
            pps = []
            for play in self.plays:
                for pp in play.play_players:
                    pps.append(pp)
            return pps

        @property
        def players(self):
            """
            A list of tuples of player data. The first element is the team
            the player was on during the game and the second element is a
            `nfldb.Player` object corresponding to that player's meta data
            (including the team he's currently on). The list is returned
            without duplicates and sorted by team and player name.
            """
            pset = set()
            players = []
            for pp in self.play_players:
                if pp.player_id not in pset:
                    players.append((pp.team, pp.player))
                    pset.add(pp.player_id)
            return sorted(players)

        @property
        def _row(self):
            return _as_row(Game._sql_columns, self)

        def _save(self, cursor):
            vals = self._row
            _upsert(cursor, 'game', vals, [vals[0]])

            # Remove any drives that are stale.
            cursor.execute('''
                DELETE FROM drive
                WHERE gsis_id = %s AND NOT (drive_id = ANY (%s))
            ''', (self.gsis_id, [d.drive_id for d in self._drives]))
            for drive in (self._drives or []):
                drive._save(cursor)

        def __str__(self):
            return '%s %d week %d on %s at %s, %s (%d) at %s (%d)' \
                   % (self.season_type, self.season_year, self.week,
                      self.start_time.strftime('%m/%d'),
                      self.start_time.strftime('%I:%M%p'),
                      self.away_team, self.away_score,
                      self.home_team, self.home_score)

#### Ancestors (in MRO)

- Game
- __builtin__.object

#### Static methods

def from_id(

db, gsis_id)

Given a GSIS identifier (e.g., `2012090500`) as a string, returns a `Game` object corresponding to `gsis_id`.

If no corresponding game is found, `None` is returned.

Show source.

    @staticmethod
    def from_id(db, gsis_id):
        """
        Given a GSIS identifier (e.g., `2012090500`) as a string,
        returns a `nfldb.Game` object corresponding to `gsis_id`.
        If no corresponding game is found, `None` is returned.
        """
        with Tx(db) as cursor:
            cursor.execute('''
                SELECT %s FROM game WHERE gsis_id = %s
            ''' % (_select_fields(Game), '%s'), (gsis_id,))
            if cursor.rowcount > 0:
                return Game.from_row(db, cursor.fetchone())
        return None

def from_row(

db, row)

Introduces a new `Game` object from a full SQL row result from the `game` table.

Show source.

    @staticmethod
    def from_row(db, row):
        """
        Introduces a new `nfldb.Game` object from a full SQL row
        result from the `game` table.
        """
        return Game(db, **row)

#### Instance variables

var away_score

The current total score for the away team.

var away_score_q1

The 1st quarter score for the away team.

var away_score_q2

The 2nd quarter score for the away team.

var away_score_q3

The 3rd quarter score for the away team.

var away_score_q4

The 4th quarter score for the away team.

var away_score_q5

The OT quarter score for the away team.

var away_team

The team abbreviation for the away team. Use the `Team` constructor to get more information on a team.

var away_turnovers

Total turnovers for the away team.

var day_of_week

The day of the week this game was played on. Possible values correspond to the `game_day` enum.

var drives

A list of `Drive`s for this game. They are automatically loaded from the database if they haven't been already.

If there are no drives found in the game, then an empty list is returned.

var finished

A boolean that is `True` if and only if the game has finished.

var gamekey

Another unique identifier for a game used by the NFL. It is a sequence number represented as a 5 character string. The gamekey is specifically used to tie games to other resources, like the NFL's content delivery network.

var gsis_id

The NFL GameCenter id of the game. It is a string with 10 characters. The first 8 correspond to the date of the game, while the last 2 correspond to an id unique to the week that the game was played.

var home_score

The current total score for the home team.

var home_score_q1

The 1st quarter score for the home team.

var home_score_q2

The 2nd quarter score for the home team.

var home_score_q3

The 3rd quarter score for the home team.

var home_score_q4

The 4th quarter score for the home team.

var home_score_q5

The OT quarter score for the home team.

var home_team

The team abbreviation for the home team. Use the `Team` constructor to get more information on a team.

var home_turnovers

Total turnovers for the home team.

var loser

The loser of this game.

var play_players

A list of `PlayPlayer` objects in this game. Data is retrieved from the database if it hasn't been already.

var players

A list of tuples of player data. The first element is the team the player was on during the game and the second element is a `Player` object corresponding to that player's meta data (including the team he's currently on). The list is returned without duplicates and sorted by team and player name.

var plays

A list of `Play` objects in this game. Data is retrieved from the database if it hasn't been already.

var season_type

The phase of the season. e.g., `Preseason`, `Regular season` or `Postseason`. All valid values correspond to the `season_phase`.

var season_year

The year of the season of this game. This does not necessarily match the year that the game was played. For example, games played in January 2013 are in season 2012.

var start_time

A Python datetime object corresponding to the start time of the game. The timezone of this value will be equivalent to the timezone specified by `set_timezone` (which is by default set to the value specified in the configuration file).

var time_inserted

The date and time that this game was added.

var time_updated

The date and time that this game was last updated.

var week

The week number of this game. It is always relative to the phase of the season. Namely, the first week of preseason is 1 and so is the first week of the regular season.

var winner

The winner of this game.

#### Methods

def __init__(

self, db, gsis_id, gamekey, start_time, week, day_of_week, season_year, season_type, finished, home_team, home_score, home_score_q1, home_score_q2, home_score_q3, home_score_q4, home_score_q5, home_turnovers, away_team, away_score, away_score_q1, away_score_q2, away_score_q3, away_score_q4, away_score_q5, away_turnovers, time_inserted, time_updated, loser=None, winner=None)

Introduces a new `Drive` object with the given attributes.

This constructor should probably not be used. Instead, use `from_row` or `from_id`.

Show source.

    def __init__(self, db, gsis_id, gamekey, start_time, week, day_of_week,
                 season_year, season_type, finished,
                 home_team, home_score, home_score_q1, home_score_q2,
                 home_score_q3, home_score_q4, home_score_q5, home_turnovers,
                 away_team, away_score, away_score_q1, away_score_q2,
                 away_score_q3, away_score_q4, away_score_q5, away_turnovers,
                 time_inserted, time_updated, loser=None, winner=None):
        """
        Introduces a new `nfldb.Drive` object with the given attributes.
        This constructor should probably not be used. Instead, use
        `nfldb.Game.from_row` or `nfldb.Game.from_id`.
        """
        self._drives = None
        self._db = db
        """
        The psycopg2 database connection.
        """
        self.gsis_id = gsis_id
        """
        The NFL GameCenter id of the game. It is a string
        with 10 characters. The first 8 correspond to the date of the
        game, while the last 2 correspond to an id unique to the week that
        the game was played.
        """
        self.gamekey = gamekey
        """
        Another unique identifier for a game used by the
        NFL. It is a sequence number represented as a 5 character string.
        The gamekey is specifically used to tie games to other resources,
        like the NFL's content delivery network.
        """
        self.start_time = start_time
        """
        A Python datetime object corresponding to the start time of
        the game. The timezone of this value will be equivalent to the
        timezone specified by `nfldb.set_timezone` (which is by default
        set to the value specified in the configuration file).
        """
        self.week = week
        """
        The week number of this game. It is always relative
        to the phase of the season. Namely, the first week of preseason
        is 1 and so is the first week of the regular season.
        """
        self.day_of_week = day_of_week
        """
        The day of the week this game was played on.
        Possible values correspond to the `nfldb.Enums.game_day` enum.
        """
        self.season_year = season_year
        """
        The year of the season of this game. This
        does not necessarily match the year that the game was played. For
        example, games played in January 2013 are in season 2012.
        """
        self.season_type = season_type
        """
        The phase of the season. e.g., `Preseason`,
        `Regular season` or `Postseason`. All valid values correspond
        to the `nfldb.Enums.season_phase`.
        """
        self.finished = finished
        """
        A boolean that is `True` if and only if the game has finished.
        """
        self.home_team = home_team
        """
        The team abbreviation for the home team. Use the `nfldb.Team`
        constructor to get more information on a team.
        """
        self.home_score = home_score
        """The current total score for the home team."""
        self.home_score_q1 = home_score_q1
        """The 1st quarter score for the home team."""
        self.home_score_q2 = home_score_q2
        """The 2nd quarter score for the home team."""
        self.home_score_q3 = home_score_q3
        """The 3rd quarter score for the home team."""
        self.home_score_q4 = home_score_q4
        """The 4th quarter score for the home team."""
        self.home_score_q5 = home_score_q5
        """The OT quarter score for the home team."""
        self.home_turnovers = home_turnovers
        """Total turnovers for the home team."""
        self.away_team = away_team
        """
        The team abbreviation for the away team. Use the `nfldb.Team`
        constructor to get more information on a team.
        """
        self.away_score = away_score
        """The current total score for the away team."""
        self.away_score_q1 = away_score_q1
        """The 1st quarter score for the away team."""
        self.away_score_q2 = away_score_q2
        """The 2nd quarter score for the away team."""
        self.away_score_q3 = away_score_q3
        """The 3rd quarter score for the away team."""
        self.away_score_q4 = away_score_q4
        """The 4th quarter score for the away team."""
        self.away_score_q5 = away_score_q5
        """The OT quarter score for the away team."""
        self.away_turnovers = away_turnovers
        """Total turnovers for the away team."""
        self.time_inserted = time_inserted
        """The date and time that this game was added."""
        self.time_updated = time_updated
        """The date and time that this game was last updated."""

class Play

Represents a single play in an NFL game. Each play has an assortment of meta data, possibly including the time on the clock in which the ball was snapped, the starting field position, the down, yards to go, etc. Not all plays have values for each field (for example, a timeout is considered a play but has no data for `down` or `yardline`).

In addition to meta data describing the context of the game at the time the ball was snapped, plays also have statistics corresponding to the fields in `stat_categories` with a `category_type` of `play`. For example, `third_down_att`, `fourth_down_failed` and `fourth_down_conv`. While the binary nature of these fields suggest a boolean value, they are actually integers. This makes them amenable to aggregation.

Plays are also associated with player statistics or "events" that occurred in a play. For example, in a single play one player could pass the ball to another player. This is recorded as two different player statistics: a pass and a reception. Each one is represented as a `PlayPlayer` object. Plays may have zero or more of these player statistics.

Finally, it is important to note that there are (currently) some useful statistics missing. For example, there is currently no reliable means of determining the time on the clock when the play finished. Also, there is no field describing the field position at the end of the play, although this may be added in the future.

Most of the statistical fields are documented on the statistical categories wiki page. Each statistical field is an instance attribute in this class.

Show source.

    class Play (object):
        """
        Represents a single play in an NFL game. Each play has an
        assortment of meta data, possibly including the time on the clock
        in which the ball was snapped, the starting field position, the
        down, yards to go, etc. Not all plays have values for each field
        (for example, a timeout is considered a play but has no data for
        `nfldb.Play.down` or `nfldb.Play.yardline`).

        In addition to meta data describing the context of the game at the time
        the ball was snapped, plays also have statistics corresponding to the
        fields in `nfldb.stat_categories` with a `nfldb.Category.category_type`
        of `play`. For example, `third_down_att`, `fourth_down_failed` and
        `fourth_down_conv`. While the binary nature of these fields suggest
        a boolean value, they are actually integers. This makes them amenable
        to aggregation.

        Plays are also associated with player statistics or "events" that
        occurred in a play. For example, in a single play one player could
        pass the ball to another player. This is recorded as two different
        player statistics: a pass and a reception. Each one is represented
        as a `nfldb.PlayPlayer` object. Plays may have **zero or more** of
        these player statistics.

        Finally, it is important to note that there are (currently) some
        useful statistics missing. For example, there is currently no
        reliable means of determining the time on the clock when the play
        finished.  Also, there is no field describing the field position at
        the end of the play, although this may be added in the future.

        Most of the statistical fields are documented on the
        [statistical categories](http://goo.gl/YY587P)
        wiki page. Each statistical field is an instance attribute in
        this class.
        """
        _table = 'play'

        _sql_columns = (['gsis_id', 'drive_id', 'play_id', 'time', 'pos_team',
                         'yardline', 'down', 'yards_to_go', 'description', 'note',
                         'time_inserted', 'time_updated',
                         ] + _play_categories.keys()
                        )

        _sql_derived = []

        _sql_fields = _sql_columns + _sql_derived

        __slots__ = _sql_fields + ['_db', '_drive', '_play_players']

        @staticmethod
        def _as_sql(field, prefix=None):
            prefix = 'play.' if prefix is None else prefix
            if field in Play._sql_columns:
                return '%s%s' % (prefix, field)
            raise AttributeError(field)

        @staticmethod
        def _from_nflgame(db, d, p):
            """
            Given `d` as a `nfldb.Drive` object and `p` as a
            `nflgame.game.Play` object, `_from_nflgame` converts `p` to a
            `nfldb.Play` object.
            """
            stats = {}
            for k in _play_categories.keys() + Play._sql_derived:
                if p._stats.get(k, 0) != 0:
                    stats[k] = p._stats[k]

            # Fix up some fields so they meet the constraints of the schema.
            # The `time` field is cleaned up afterwards in
            # `nfldb.Drive._from_nflgame`, since it needs data about surrounding
            # plays.
            time = None if not p.time else _nflgame_clock(p.time)
            yardline = FieldPosition(getattr(p.yardline, 'offset', None))
            down = p.down if 1 <= p.down <= 4 else None
            team = p.team if p.team is not None and len(p.team) > 0 else 'UNK'
            play = Play(db, d.gsis_id, d.drive_id, int(p.playid), time, team,
                        yardline, down, p.yards_togo, p.desc, p.note,
                        None, None, stats)

            play._drive = d
            play._play_players = []
            for pp in p.players:
                play._play_players.append(PlayPlayer._from_nflgame(db, play, pp))
            return play

        @staticmethod
        def _from_tuple(db, t):
            """
            Introduces a new `nfldb.Play` object from a tuple SQL
            result. This constructor exists for performance reasons and
            is only used inside the `nfldb.Query` class. In particular,
            the order of the fields in the originating SELECT query is
            significant.
            """
            cols = Play._sql_fields
            stats = {}
            for i, v in enumerate(t[12:], 12):
                if v != 0:
                    stats[cols[i]] = v
            return Play(db, t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7], t[8],
                        t[9], t[10], t[11], stats)

        @staticmethod
        def from_row(db, row):
            """
            Introduces a new `nfldb.Play` object from a full SQL row result
            from the `play` table.
            """
            stats = {}
            get = row.get
            for cat in _play_categories:
                if get(cat, 0) != 0:
                    stats[cat] = row[cat]
            return Play(db, row['gsis_id'], row['drive_id'], row['play_id'],
                        row['time'], row['pos_team'], row['yardline'],
                        row['down'], row['yards_to_go'], row['description'],
                        row['note'], row['time_inserted'], row['time_updated'],
                        stats)

        @staticmethod
        def from_id(db, gsis_id, drive_id, play_id):
            """
            Given a GSIS identifier (e.g., `2012090500`) as a string,
            an integer drive id and an integer play id, this returns a
            `nfldb.Play` object corresponding to the given identifiers.

            If no corresponding play is found, then `None` is returned.
            """
            with Tx(db) as cursor:
                q = '''
                    SELECT %s FROM play WHERE (gsis_id, drive_id, play_id) = %s
                ''' % (_select_fields(Play), '%s')
                cursor.execute(q, ((gsis_id, drive_id, play_id),))
                if cursor.rowcount > 0:
                    return Play.from_row(db, cursor.fetchone())
            return None

        def __init__(self, db, gsis_id, drive_id, play_id, time, pos_team,
                     yardline, down, yards_to_go, description, note,
                     time_inserted, time_updated, stats):
            """
            Introduces a new `nfldb.Play` object with the given
            attributes.

            `stats` should be a dictionary of statistical play categories
            from `nfldb.stat_categories`.

            This constructor should probably not be used. Instead, use
            `nfldb.Play.from_row` or `nfldb.Play.from_id`. Alternatively,
            the `nfldb.Game`.`nfldb.Game.plays` and
            `nfldb.Drive`.`nfldb.Drive.plays` attributes can be used to get
            plays in a game or a drive, respectively.
            """
            self._drive = None
            self._play_players = None
            self._db = db

            self.gsis_id = gsis_id
            """
            The GSIS identifier for the game that this play belongs to.
            """
            self.drive_id = drive_id
            """
            The numeric drive identifier for this play. It may be
            interpreted as a sequence number.
            """
            self.play_id = play_id
            """
            The numeric play identifier for this play. It can typically
            be interpreted as a sequence number scoped to the week that
            this game was played, but it's unfortunately not completely
            consistent.
            """
            self.time = time
            """
            The time on the clock when the play started, represented with
            a `nfldb.Clock` object.
            """
            self.pos_team = pos_team
            """
            The team in possession during this play, represented as
            a team abbreviation string. Use the `nfldb.Team` constructor
            to get more information on a team.
            """
            self.yardline = yardline
            """
            The starting field position of this play represented with
            `nfldb.FieldPosition`.
            """
            self.down = down
            """
            The down on which this play begin. This may be `0` for
            "special" plays like timeouts or 2 point conversions.
            """
            self.yards_to_go = yards_to_go
            """
            The number of yards to go to get a first down or score a
            touchdown at the start of the play.
            """
            self.description = description
            """
            A (basically) free-form text description of the play. This is
            typically what you see on NFL GameCenter web pages.
            """
            self.note = note
            """
            A miscellaneous note field (as a string). Not sure what it's
            used for.
            """
            self.time_inserted = time_inserted
            """
            The date and time that this play was added to the
            database. This can be very useful when sorting plays by the
            order in which they occurred in real time. Unfortunately, such
            a sort requires that play data is updated relatively close to
            when it actually occurred.
            """
            self.time_updated = time_updated
            """The date and time that this play was last updated."""

            seta = setattr
            for cat in stats:
                seta(self, cat, stats[cat])

        @property
        def drive(self):
            """
            The `nfldb.Drive` object that contains this play. The drive is
            retrieved from the database if it hasn't been already.
            """
            if self._drive is None:
                self._drive = Drive.from_id(self._db, self.gsis_id, self.drive_id)
            return self._drive

        @property
        def play_players(self):
            """
            A list of all `nfldb.PlayPlayer`s in this play. They are
            automatically retrieved from the database if they haven't been
            already.

            If there are no players attached to this play, then an empty
            list is returned.
            """
            if self._play_players is None:
                self._play_players = []
                with Tx(self._db) as cursor:
                    q = '''
                        SELECT %s FROM play_player
                        WHERE (gsis_id, drive_id, play_id) = %s
                    ''' % (_select_fields(PlayPlayer), '%s')
                    cursor.execute(
                        q, ((self.gsis_id, self.drive_id, self.play_id),))
                    for row in cursor.fetchall():
                        pp = PlayPlayer.from_row(self._db, row)
                        pp._play = self
                        self._play_players.append(pp)
            return self._play_players

        @property
        def _row(self):
            return _as_row(Play._sql_columns, self)

        def _save(self, cursor):
            vals = self._row
            _upsert(cursor, 'play', vals, vals[0:3])

            # Remove any "play players" that are stale.
            cursor.execute('''
                DELETE FROM play_player
                WHERE gsis_id = %s AND drive_id = %s AND play_id = %s
                      AND NOT (player_id = ANY (%s))
            ''', (self.gsis_id, self.drive_id, self.play_id,
                  [p.player_id for p in (self._play_players or [])]))
            for pp in (self._play_players or []):
                pp._save(cursor)

        def __str__(self):
            if self.down:
                return '(%s, %s, %s, %d and %d) %s' \
                       % (self.pos_team, self.yardline, self.time.phase,
                          self.down, self.yards_to_go, self.description)
            elif self.pos_team:
                return '(%s, %s, %s) %s' \
                       % (self.pos_team, self.yardline, self.time.phase,
                          self.description)
            else:
                return '(%s) %s' % (self.time.phase, self.description)

        def __getattr__(self, k):
            if k in Play.__slots__:
                return 0
            if k in PlayPlayer.__slots__:
                for pp in self.play_players:
                    v = getattr(pp, k)
                    if v != 0:
                        return v
                return 0
            raise AttributeError(k)

#### Ancestors (in MRO)

- Play
- __builtin__.object

#### Static methods

def from_id(

db, gsis_id, drive_id, play_id)

Given a GSIS identifier (e.g., `2012090500`) as a string, an integer drive id and an integer play id, this returns a `Play` object corresponding to the given identifiers.

If no corresponding play is found, then `None` is returned.

Show source.

    @staticmethod
    def from_id(db, gsis_id, drive_id, play_id):
        """
        Given a GSIS identifier (e.g., `2012090500`) as a string,
        an integer drive id and an integer play id, this returns a
        `nfldb.Play` object corresponding to the given identifiers.
        If no corresponding play is found, then `None` is returned.
        """
        with Tx(db) as cursor:
            q = '''
                SELECT %s FROM play WHERE (gsis_id, drive_id, play_id) = %s
            ''' % (_select_fields(Play), '%s')
            cursor.execute(q, ((gsis_id, drive_id, play_id),))
            if cursor.rowcount > 0:
                return Play.from_row(db, cursor.fetchone())
        return None

def from_row(

db, row)

Introduces a new `Play` object from a full SQL row result from the `play` table.

Show source.

    @staticmethod
    def from_row(db, row):
        """
        Introduces a new `nfldb.Play` object from a full SQL row result
        from the `play` table.
        """
        stats = {}
        get = row.get
        for cat in _play_categories:
            if get(cat, 0) != 0:
                stats[cat] = row[cat]
        return Play(db, row['gsis_id'], row['drive_id'], row['play_id'],
                    row['time'], row['pos_team'], row['yardline'],
                    row['down'], row['yards_to_go'], row['description'],
                    row['note'], row['time_inserted'], row['time_updated'],
                    stats)

#### Instance variables

var description

A (basically) free-form text description of the play. This is typically what you see on NFL GameCenter web pages.

var down

The down on which this play begin. This may be `0` for "special" plays like timeouts or 2 point conversions.

var drive

The `Drive` object that contains this play. The drive is retrieved from the database if it hasn't been already.

var drive_id

The numeric drive identifier for this play. It may be interpreted as a sequence number.

var gsis_id

The GSIS identifier for the game that this play belongs to.

var note

A miscellaneous note field (as a string). Not sure what it's used for.

var play_id

The numeric play identifier for this play. It can typically be interpreted as a sequence number scoped to the week that this game was played, but it's unfortunately not completely consistent.

var play_players

A list of all `PlayPlayer`s in this play. They are automatically retrieved from the database if they haven't been already.

If there are no players attached to this play, then an empty list is returned.

var pos_team

The team in possession during this play, represented as a team abbreviation string. Use the `Team` constructor to get more information on a team.

var time

The time on the clock when the play started, represented with a `Clock` object.

var time_inserted

The date and time that this play was added to the database. This can be very useful when sorting plays by the order in which they occurred in real time. Unfortunately, such a sort requires that play data is updated relatively close to when it actually occurred.

var time_updated

The date and time that this play was last updated.

var yardline

The starting field position of this play represented with `FieldPosition`.

var yards_to_go

The number of yards to go to get a first down or score a touchdown at the start of the play.

#### Methods

def __init__(

self, db, gsis_id, drive_id, play_id, time, pos_team, yardline, down, yards_to_go, description, note, time_inserted, time_updated, stats)

Introduces a new `Play` object with the given attributes.

`stats` should be a dictionary of statistical play categories from `stat_categories`.

This constructor should probably not be used. Instead, use `from_row` or `from_id`. Alternatively, the `Game`.`plays` and `Drive`.`plays` attributes can be used to get plays in a game or a drive, respectively.

Show source.

    def __init__(self, db, gsis_id, drive_id, play_id, time, pos_team,
                 yardline, down, yards_to_go, description, note,
                 time_inserted, time_updated, stats):
        """
        Introduces a new `nfldb.Play` object with the given
        attributes.
        `stats` should be a dictionary of statistical play categories
        from `nfldb.stat_categories`.
        This constructor should probably not be used. Instead, use
        `nfldb.Play.from_row` or `nfldb.Play.from_id`. Alternatively,
        the `nfldb.Game`.`nfldb.Game.plays` and
        `nfldb.Drive`.`nfldb.Drive.plays` attributes can be used to get
        plays in a game or a drive, respectively.
        """
        self._drive = None
        self._play_players = None
        self._db = db
        self.gsis_id = gsis_id
        """
        The GSIS identifier for the game that this play belongs to.
        """
        self.drive_id = drive_id
        """
        The numeric drive identifier for this play. It may be
        interpreted as a sequence number.
        """
        self.play_id = play_id
        """
        The numeric play identifier for this play. It can typically
        be interpreted as a sequence number scoped to the week that
        this game was played, but it's unfortunately not completely
        consistent.
        """
        self.time = time
        """
        The time on the clock when the play started, represented with
        a `nfldb.Clock` object.
        """
        self.pos_team = pos_team
        """
        The team in possession during this play, represented as
        a team abbreviation string. Use the `nfldb.Team` constructor
        to get more information on a team.
        """
        self.yardline = yardline
        """
        The starting field position of this play represented with
        `nfldb.FieldPosition`.
        """
        self.down = down
        """
        The down on which this play begin. This may be `0` for
        "special" plays like timeouts or 2 point conversions.
        """
        self.yards_to_go = yards_to_go
        """
        The number of yards to go to get a first down or score a
        touchdown at the start of the play.
        """
        self.description = description
        """
        A (basically) free-form text description of the play. This is
        typically what you see on NFL GameCenter web pages.
        """
        self.note = note
        """
        A miscellaneous note field (as a string). Not sure what it's
        used for.
        """
        self.time_inserted = time_inserted
        """
        The date and time that this play was added to the
        database. This can be very useful when sorting plays by the
        order in which they occurred in real time. Unfortunately, such
        a sort requires that play data is updated relatively close to
        when it actually occurred.
        """
        self.time_updated = time_updated
        """The date and time that this play was last updated."""
        seta = setattr
        for cat in stats:
            seta(self, cat, stats[cat])

class PlayPlayer

A "play player" is a statistical grouping of categories for a single player inside a play. For example, passing the ball to a receiver necessarily requires two "play players": the pass (by player X) and the reception (by player Y). Statistics that aren't included, for example, are blocks and penalties. (Although penalty information can be gleaned from a play's free-form `description` attribute.)

Each `PlayPlayer` object belongs to exactly one `Play` and exactly one `Player`.

Any statistical categories not relevant to this particular play and player default to `0`.

Most of the statistical fields are documented on the statistical categories wiki page. Each statistical field is an instance attribute in this class.

Show source.

    class PlayPlayer (object):
        """
        A "play player" is a statistical grouping of categories for a
        single player inside a play. For example, passing the ball to
        a receiver necessarily requires two "play players": the pass
        (by player X) and the reception (by player Y). Statistics that
        aren't included, for example, are blocks and penalties. (Although
        penalty information can be gleaned from a play's free-form
        `nfldb.Play.description` attribute.)

        Each `nfldb.PlayPlayer` object belongs to exactly one
        `nfldb.Play` and exactly one `nfldb.Player`.

        Any statistical categories not relevant to this particular play
        and player default to `0`.

        Most of the statistical fields are documented on the
        [statistical categories](http://goo.gl/wZstcY)
        wiki page. Each statistical field is an instance attribute in
        this class.
        """
        _table = 'play_player'

        _sql_columns = (['gsis_id', 'drive_id', 'play_id', 'player_id', 'team']
                        + _player_categories.keys()
                        )

        _sql_derived = ['offense_yds', 'offense_tds', 'defense_tds']

        # Define various additive combinations of fields.
        # Abuse the additive identity.
        _derived_sums = {
            'offense_yds': ['passing_yds', 'rushing_yds', 'receiving_yds',
                            'fumbles_rec_yds'],
            'offense_tds': ['passing_tds', 'receiving_tds', 'rushing_tds',
                            'fumbles_rec_tds'],
            'defense_tds': ['defense_frec_tds', 'defense_int_tds',
                            'defense_misc_tds'],
        }

        _sql_fields = _sql_columns + _sql_derived

        __slots__ = _sql_fields + ['_db', '_play', '_player']

        # Document instance variables for derived SQL fields.
        # We hide them from the public interface, but make the doco
        # available to nfldb-mk-stat-table. Evil!
        __pdoc__['PlayPlayer.offense_yds'] = None
        __pdoc__['_PlayPlayer.offense_yds'] = \
            '''
            Corresponds to any yardage that is manufactured by the offense.
            Namely, the following fields:
            `nfldb.PlayPlayer.passing_yds`,
            `nfldb.PlayPlayer.rushing_yds`,
            `nfldb.PlayPlayer.receiving_yds` and
            `nfldb.PlayPlayer.fumbles_rec_yds`.

            This field is useful when searching for plays by net yardage
            regardless of how the yards were obtained.
            '''
        __pdoc__['PlayPlayer.offense_tds'] = None
        __pdoc__['_PlayPlayer.offense_tds'] = \
            '''
            Corresponds to any touchdown manufactured by the offense via
            a passing, reception, rush or fumble recovery.
            '''
        __pdoc__['PlayPlayer.defense_tds'] = None
        __pdoc__['_PlayPlayer.defense_tds'] = \
            '''
            Corresponds to any touchdown manufactured by the defense.
            e.g., a pick-6, fumble recovery TD, punt/FG block TD, etc.
            '''

        @staticmethod
        def _as_sql(field, prefix=None):
            prefix = 'play_player.' if prefix is None else prefix
            if field in PlayPlayer._sql_columns:
                return '%s%s' % (prefix, field)
            elif field in PlayPlayer._derived_sums:
                tosum = PlayPlayer._derived_sums[field]
                return ' + '.join('%s%s' % (prefix, f) for f in tosum)
            raise AttributeError(field)

        @staticmethod
        def _from_nflgame(db, p, pp):
            """
            Given `p` as a `nfldb.Play` object and `pp` as a
            `nflgame.player.PlayPlayerStats` object, `_from_nflgame`
            converts `pp` to a `nfldb.PlayPlayer` object.
            """
            stats = {}
            for k in _player_categories.keys() + PlayPlayer._sql_derived:
                if pp._stats.get(k, 0) != 0:
                    stats[k] = pp._stats[k]

            team = nfldb.team.standard_team(pp.team)
            play_player = PlayPlayer(db, p.gsis_id, p.drive_id, p.play_id,
                                     pp.playerid, team, stats)
            play_player._play = p
            play_player._player = Player._from_nflgame(db, pp)
            return play_player

        @staticmethod
        def _from_tuple(db, t):
            """
            Introduces a new `nfldb.PlayPlayer` object from a tuple SQL
            result. This constructor exists for performance reasons and
            is only used inside the `nfldb.Query` class. In particular,
            the order of the fields in the originating SELECT query is
            significant.
            """
            cols = PlayPlayer._sql_fields
            stats = {}
            for i, v in enumerate(t[5:], 5):
                if v != 0:
                    stats[cols[i]] = v
            return PlayPlayer(db, t[0], t[1], t[2], t[3], t[4], stats)

        @staticmethod
        def from_row(db, row):
            """
            Introduces a new `nfldb.PlayPlayer` object from a full SQL row
            result from the `play_player` table.
            """
            return PlayPlayer(db, row['gsis_id'], row['drive_id'],
                              row['play_id'], row['player_id'], row['team'], row)

        def __init__(self, db, gsis_id, drive_id, play_id, player_id, team,
                     stats):
            """
            Introduces a new `nfldb.PlayPlayer` object with the given
            attributes. `stats` should eb a dictionary mapping player
            statistical categories in `nfldb.stat_categories` to their
            corresponding values.

            This constructor should probably not be used. Instead, use
            `nfldb.PlayPlayer.from_row`, or more conveniently, any of
            the following `play_players` attributes:
            `nfldb.Game`.`nfldb.Game.play_players`,
            `nfldb.Drive`.`nfldb.Drive.play_players` or
            `nfldb.Play`.`nfldb.Play.play_players`.
            """
            self._play = None
            self._player = None
            self._db = db

            self.gsis_id = gsis_id
            """
            The GSIS identifier for the game that this "play player"
            belongs to.
            """
            self.drive_id = drive_id
            """
            The numeric drive identifier for this "play player". It may be
            interpreted as a sequence number.
            """
            self.play_id = play_id
            """
            The numeric play identifier for this "play player". It can
            typically be interpreted as a sequence number scoped to its
            corresponding game.
            """
            self.player_id = player_id
            """
            The player_id linking these stats to a `nfldb.Player` object.
            Use `nfldb.PlayPlayer.player` to access player meta data.

            N.B. This is the GSIS identifier string. It always has length
            10.
            """
            self.team = team
            """
            The team that this player belonged to when he recorded the
            statistics in this play.
            """
            seta = setattr
            for cat in stats:
                seta(self, cat, stats[cat])

        @property
        def play(self):
            """
            The `nfldb.Play` object that this "play player" belongs
            to. The play is retrieved from the database if necessary.
            """
            if self._play is None:
                self._play = Play.from_id(self._db, self.gsis_id, self.drive_id,
                                          self._play_id)
            return self._play

        @property
        def player(self):
            """
            The `nfldb.Player` object that this "play player"
            corresponds to. The player is retrieved from the database if
            necessary.
            """
            if self._player is None:
                self._player = Player.from_id(self._db, self.player_id)
            return self._player

        @property
        def _row(self):
            return _as_row(PlayPlayer._sql_columns, self)

        def _save(self, cursor):
            vals = self._row
            _upsert(cursor, 'play_player', vals, vals[0:4])
            if self._player is not None:
                self._player._save(cursor)

        def _add(self, b):
            """
            Given two `nfldb.PlayPlayer` objects, `_add` accumulates `b`
            into `self`. Namely, no new `nfldb.PlayPlayer` objects are
            created.

            Both `self` and `b` must refer to the same player, or else an
            assertion error is raised.

            The `nfldb.aggregate` function should be used to sum collections
            of `nfldb.PlayPlayer` objects (or objects that can provide
            `nfldb.PlayPlayer` objects).
            """
            a = self
            assert a.player_id == b.player_id
            a.gsis_id = a.gsis_id if a.gsis_id == b.gsis_id else None
            a.drive_id = a.drive_id if a.drive_id == b.drive_id else None
            a.play_id = a.play_id if a.play_id == b.play_id else None
            a.team = a.team if a.team == b.team else None

            for cat in _player_categories:
                s = getattr(a, cat) + getattr(b, cat)
                if s != 0:
                    setattr(a, cat, s)

            # Try to copy player meta data too.
            if a._player is None and b._player is not None:
                a._player = b._player

            # A play attached to aggregate statistics is always wrong.
            a._play = None

        def _copy(self):
            """Returns a copy of `self`."""
            stats = dict([(k, getattr(self, k, 0)) for k in _player_categories])
            pp = PlayPlayer(self._db, self.gsis_id, self.drive_id, self.play_id,
                            self.player_id, self.team, stats)
            pp._player = self._player
            pp._play = self._play
            return pp

        def __add__(self, b):
            pp = self._copy()
            pp.add(b)
            return pp

        def __str__(self):
            d = {}
            for cat in _player_categories:
                v = getattr(self, cat, 0)
                if v != 0:
                    d[cat] = v
            return repr(d)

        def __getattr__(self, k):
            if k in PlayPlayer.__slots__:
                return 0
            raise AttributeError

#### Ancestors (in MRO)

- PlayPlayer
- __builtin__.object

#### Static methods

def from_row(

db, row)

Introduces a new `PlayPlayer` object from a full SQL row result from the `play_player` table.

Show source.

    @staticmethod
    def from_row(db, row):
        """
        Introduces a new `nfldb.PlayPlayer` object from a full SQL row
        result from the `play_player` table.
        """
        return PlayPlayer(db, row['gsis_id'], row['drive_id'],
                          row['play_id'], row['player_id'], row['team'], row)

#### Instance variables

var drive_id

The numeric drive identifier for this "play player". It may be interpreted as a sequence number.

var gsis_id

The GSIS identifier for the game that this "play player" belongs to.

var play

The `Play` object that this "play player" belongs to. The play is retrieved from the database if necessary.

var play_id

The numeric play identifier for this "play player". It can typically be interpreted as a sequence number scoped to its corresponding game.

var player

The `Player` object that this "play player" corresponds to. The player is retrieved from the database if necessary.

var player_id

The player_id linking these stats to a `Player` object. Use `player` to access player meta data.

N.B. This is the GSIS identifier string. It always has length 10.

var team

The team that this player belonged to when he recorded the statistics in this play.

#### Methods

def __init__(

self, db, gsis_id, drive_id, play_id, player_id, team, stats)

Introduces a new `PlayPlayer` object with the given attributes. `stats` should eb a dictionary mapping player statistical categories in `stat_categories` to their corresponding values.

This constructor should probably not be used. Instead, use `from_row`, or more conveniently, any of the following `play_players` attributes: `Game`.`play_players`, `Drive`.`play_players` or `Play`.`play_players`.

Show source.

    def __init__(self, db, gsis_id, drive_id, play_id, player_id, team,
                 stats):
        """
        Introduces a new `nfldb.PlayPlayer` object with the given
        attributes. `stats` should eb a dictionary mapping player
        statistical categories in `nfldb.stat_categories` to their
        corresponding values.
        This constructor should probably not be used. Instead, use
        `nfldb.PlayPlayer.from_row`, or more conveniently, any of
        the following `play_players` attributes:
        `nfldb.Game`.`nfldb.Game.play_players`,
        `nfldb.Drive`.`nfldb.Drive.play_players` or
        `nfldb.Play`.`nfldb.Play.play_players`.
        """
        self._play = None
        self._player = None
        self._db = db
        self.gsis_id = gsis_id
        """
        The GSIS identifier for the game that this "play player"
        belongs to.
        """
        self.drive_id = drive_id
        """
        The numeric drive identifier for this "play player". It may be
        interpreted as a sequence number.
        """
        self.play_id = play_id
        """
        The numeric play identifier for this "play player". It can
        typically be interpreted as a sequence number scoped to its
        corresponding game.
        """
        self.player_id = player_id
        """
        The player_id linking these stats to a `nfldb.Player` object.
        Use `nfldb.PlayPlayer.player` to access player meta data.
        N.B. This is the GSIS identifier string. It always has length
        10.
        """
        self.team = team
        """
        The team that this player belonged to when he recorded the
        statistics in this play.
        """
        seta = setattr
        for cat in stats:
            seta(self, cat, stats[cat])

class Player

A representation of an NFL player. Note that the representation is inherently ephemeral; it always corresponds to the most recent knowledge about a player.

Most of the fields in this object can have a `None` value. This is because the source JSON data only guarantees that a GSIS identifier and abbreviated name will be available. The rest of the player meta data is scraped from NFL.com's team roster pages (which invites infrequent uncertainty).

Show source.

    class Player (object):
        """
        A representation of an NFL player. Note that the representation
        is inherently ephemeral; it always corresponds to the most recent
        knowledge about a player.

        Most of the fields in this object can have a `None` value. This is
        because the source JSON data only guarantees that a GSIS identifier
        and abbreviated name will be available. The rest of the player meta
        data is scraped from NFL.com's team roster pages (which invites
        infrequent uncertainty).
        """
        _table = 'player'

        _sql_columns = ['player_id', 'gsis_name', 'full_name', 'first_name',
                        'last_name', 'team', 'position', 'profile_id',
                        'profile_url', 'uniform_number', 'birthdate', 'college',
                        'height', 'weight', 'years_pro', 'status',
                        ]

        _sql_derived = []

        _sql_fields = _sql_columns + _sql_derived

        __slots__ = _sql_fields + ['_db']

        __existing = None
        """
        A cache of existing player ids in the database.
        This is only used when saving data to detect if a player
        needs to be added.
        """

        @staticmethod
        def _as_sql(field, prefix=None):
            prefix = 'player.' if prefix is None else prefix
            if field in Player._sql_columns:
                return '%s%s' % (prefix, field)
            raise AttributeError(field)

        @staticmethod
        def _from_nflgame(db, p):
            """
            Given `p` as a `nflgame.player.PlayPlayerStats` object,
            `_from_nflgame` converts `p` to a `nfldb.Player` object.
            """
            meta = ['full_name', 'first_name', 'last_name', 'team', 'position',
                    'profile_id', 'profile_url', 'uniform_number', 'birthdate',
                    'college', 'height', 'weight', 'years_pro', 'status']
            kwargs = {}
            if p.player is not None:
                for k in meta:
                    v = getattr(p.player, k, '')
                    if not v:
                        v = None
                    kwargs[k] = v

                # Convert position and status values to an enumeration.
                kwargs['position'] = getattr(Enums.player_pos,
                                             kwargs['position'] or '',
                                             Enums.player_pos.UNK)

                trans = Enums._nflgame_player_status
                kwargs['status'] = trans.get(kwargs['status'] or '',
                                             Enums.player_status.Unknown)

            if kwargs.get('position', None) is None:
                kwargs['position'] = Enums.player_pos.UNK
            if kwargs.get('status', None) is None:
                kwargs['status'] = Enums.player_status.Unknown

            kwargs['team'] = nfldb.team.standard_team(kwargs.get('team', ''))
            return Player(db, p.playerid, p.name, **kwargs)

        @staticmethod
        def _from_nflgame_player(db, p):
            """
            Given `p` as a `nflgame.player.Player` object,
            `_from_nflgame_player` converts `p` to a `nfldb.Player` object.
            """
            class _Player (object):
                def __init__(self):
                    self.playerid = p.player_id
                    self.name = p.gsis_name
                    self.player = p
            return Player._from_nflgame(db, _Player())

        @staticmethod
        def from_row(db, r):
            """
            Introduces a `nfldb.Player` object from a full SQL row from the
            `player` table.
            """
            return Player(db, r['player_id'], r['gsis_name'], r['full_name'],
                          r['first_name'], r['last_name'], r['team'],
                          r['position'], r['profile_id'], r['profile_url'],
                          r['uniform_number'], r['birthdate'], r['college'],
                          r['height'], r['weight'], r['years_pro'], r['status'])

        @staticmethod
        def from_id(db, player_id):
            """
            Given a player GSIS identifier (e.g., `00-0019596`) as a string,
            returns a `nfldb.Player` object corresponding to `player_id`.
            This function will always execute a single SQL query.

            If no corresponding player is found, `None` is returned.
            """
            with Tx(db) as cursor:
                cursor.execute('''
                    SELECT %s FROM player WHERE player_id = %s
                ''' % (_select_fields(Player), '%s'), (player_id,))
                if cursor.rowcount > 0:
                    return Player.from_row(db, cursor.fetchone())
            return None

        def __init__(self, db, player_id, gsis_name, full_name=None,
                     first_name=None, last_name=None, team=None, position=None,
                     profile_id=None, profile_url=None, uniform_number=None,
                     birthdate=None, college=None, height=None, weight=None,
                     years_pro=None, status=None):
            """
            Introduces a new `nfldb.Player` object with the given
            attributes.

            A player object contains data known about a player as
            person. Namely, this object is not responsible for containing
            statistical data related to a player at some particular point
            in time.

            This constructor should probably not be used. Instead,
            use the more convenient constructors `Player.from_row` or
            `Player.from_id`. Alternatively, a `nfldb.Player` object can be
            obtained from the `nfldb.PlayPlayer`.`nfldb.PlayPlayer.player`
            attribute.
            """
            self._db = db

            self.player_id = player_id
            """
            The player_id linking this object `nfldb.PlayPlayer` object.

            N.B. This is the GSIS identifier string. It always has length
            10.
            """
            self.gsis_name = gsis_name
            """
            The name of a player from the source GameCenter data. This
            field is guaranteed to contain a name.
            """
            self.full_name = full_name
            """The full name of a player."""
            self.first_name = first_name
            """The first name of a player."""
            self.last_name = last_name
            """The last name of a player."""
            self.team = team
            """
            The team that the player is currently active on. If the player
            is no longer playing or is a free agent, this value may
            correspond to the `UNK` (unknown) team.
            """
            self.position = position
            """
            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 = profile_id
            """
            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 = profile_url
            """The NFL.com profile URL for this player."""
            self.uniform_number = uniform_number
            """A player's uniform number as an integer."""
            self.birthdate = birthdate
            """A player's birth date as a free-form string."""
            self.college = college
            """A player's college as a free-form string."""
            self.height = height
            """A player's height as a free-form string."""
            self.weight = weight
            """A player's weight as a free-form string."""
            self.years_pro = years_pro
            """The number of years a player has played as an integer."""
            self.status = status
            """The current status of this player as a free-form string."""

        @property
        def _row(self):
            return _as_row(Player._sql_columns, self)

        def _save(self, cursor):
            if Player.__existing is None:
                Player.__existing = set()
                cursor.execute('SELECT player_id FROM player')
                for row in cursor.fetchall():
                    Player.__existing.add(row['player_id'])
            if self.player_id not in Player.__existing:
                vals = self._row
                _upsert(cursor, 'player', vals, [vals[0]])
                Player.__existing.add(self.player_id)

        def __str__(self):
            name = self.full_name if self.full_name else self.gsis_name
            if not name:
                name = self.player_id  # Yikes.
            return '%s (%s, %s)' % (name, self.team, self.position)

        def __lt__(self, other):
            if self.__class__ is not other.__class__:
                return NotImplemented
            if self.full_name and other.full_name:
                return self.full_name < other.full_name
            return self.gsis_name < other.gsis_name

        def __eq__(self, other):
            if self.__class__ is not other.__class__:
                return NotImplemented
            return self.player_id == other.player_id

#### Ancestors (in MRO)

- Player
- __builtin__.object

#### Static methods

def from_id(

db, player_id)

Given a player GSIS identifier (e.g., `00-0019596`) as a string, returns a `Player` object corresponding to `player_id`. This function will always execute a single SQL query.

If no corresponding player is found, `None` is returned.

Show source.

    @staticmethod
    def from_id(db, player_id):
        """
        Given a player GSIS identifier (e.g., `00-0019596`) as a string,
        returns a `nfldb.Player` object corresponding to `player_id`.
        This function will always execute a single SQL query.
        If no corresponding player is found, `None` is returned.
        """
        with Tx(db) as cursor:
            cursor.execute('''
                SELECT %s FROM player WHERE player_id = %s
            ''' % (_select_fields(Player), '%s'), (player_id,))
            if cursor.rowcount > 0:
                return Player.from_row(db, cursor.fetchone())
        return None

def from_row(

db, r)

Introduces a `Player` object from a full SQL row from the `player` table.

Show source.

    @staticmethod
    def from_row(db, r):
        """
        Introduces a `nfldb.Player` object from a full SQL row from the
        `player` table.
        """
        return Player(db, r['player_id'], r['gsis_name'], r['full_name'],
                      r['first_name'], r['last_name'], r['team'],
                      r['position'], r['profile_id'], r['profile_url'],
                      r['uniform_number'], r['birthdate'], r['college'],
                      r['height'], r['weight'], r['years_pro'], r['status'])

#### Instance variables

var birthdate

A player's birth date as a free-form string.

var college

A player's college as a free-form string.

var first_name

The first name of a player.

var full_name

The full name of a player.

var gsis_name

The name of a player from the source GameCenter data. This field is guaranteed to contain a name.

var height

A player's height as a free-form string.

var last_name

The last name of a player.

var player_id

The player_id linking this object `PlayPlayer` object.

N.B. This is the GSIS identifier string. It always has length 10.

var position

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 `player_pos`.

var profile_id

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.

var profile_url

The NFL.com profile URL for this player.

var status

The current status of this player as a free-form string.

var team

The team that the player is currently active on. If the player is no longer playing or is a free agent, this value may correspond to the `UNK` (unknown) team.

var uniform_number

A player's uniform number as an integer.

var weight

A player's weight as a free-form string.

var years_pro

The number of years a player has played as an integer.

#### Methods

def __init__(

self, db, player_id, gsis_name, full_name=None, first_name=None, last_name=None, team=None, position=None, profile_id=None, profile_url=None, uniform_number=None, birthdate=None, college=None, height=None, weight=None, years_pro=None, status=None)

Introduces a new `Player` object with the given attributes.

A player object contains data known about a player as person. Namely, this object is not responsible for containing statistical data related to a player at some particular point in time.

This constructor should probably not be used. Instead, use the more convenient constructors `Player.from_row` or `Player.from_id`. Alternatively, a `Player` object can be obtained from the `PlayPlayer`.`player` attribute.

Show source.

    def __init__(self, db, player_id, gsis_name, full_name=None,
                 first_name=None, last_name=None, team=None, position=None,
                 profile_id=None, profile_url=None, uniform_number=None,
                 birthdate=None, college=None, height=None, weight=None,
                 years_pro=None, status=None):
        """
        Introduces a new `nfldb.Player` object with the given
        attributes.
        A player object contains data known about a player as
        person. Namely, this object is not responsible for containing
        statistical data related to a player at some particular point
        in time.
        This constructor should probably not be used. Instead,
        use the more convenient constructors `Player.from_row` or
        `Player.from_id`. Alternatively, a `nfldb.Player` object can be
        obtained from the `nfldb.PlayPlayer`.`nfldb.PlayPlayer.player`
        attribute.
        """
        self._db = db
        self.player_id = player_id
        """
        The player_id linking this object `nfldb.PlayPlayer` object.
        N.B. This is the GSIS identifier string. It always has length
        10.
        """
        self.gsis_name = gsis_name
        """
        The name of a player from the source GameCenter data. This
        field is guaranteed to contain a name.
        """
        self.full_name = full_name
        """The full name of a player."""
        self.first_name = first_name
        """The first name of a player."""
        self.last_name = last_name
        """The last name of a player."""
        self.team = team
        """
        The team that the player is currently active on. If the player
        is no longer playing or is a free agent, this value may
        correspond to the `UNK` (unknown) team.
        """
        self.position = position
        """
        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 = profile_id
        """
        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 = profile_url
        """The NFL.com profile URL for this player."""
        self.uniform_number = uniform_number
        """A player's uniform number as an integer."""
        self.birthdate = birthdate
        """A player's birth date as a free-form string."""
        self.college = college
        """A player's college as a free-form string."""
        self.height = height
        """A player's height as a free-form string."""
        self.weight = weight
        """A player's weight as a free-form string."""
        self.years_pro = years_pro
        """The number of years a player has played as an integer."""
        self.status = status
        """The current status of this player as a free-form string."""

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.

Show source.

    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 clock(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 clock(

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.

Show source.

    @staticmethod
    def clock(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.

Show source.

    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:

    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:

    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:

    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:

    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:

    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:

    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.

Show source.

    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._sort_tables = []
            """The tables to restrain limiting criteria 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

        @property
        def _sorter(self):
            return Sorter(self._sort_exprs, self._limit,
                          restraining=self._sort_tables)

        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 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.
            """
            _append_conds(self._default_cond, types.Game, kw)
            if 'team' in kw:
                ors = {'home_team': kw['team'], 'away_team': kw['team']}
                self.andalso(Query(self._db, orelse=True).game(**ors))
            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)
            _append_conds(self._default_cond, types.PlayPlayer, kw)

            # 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(**ors))

                if nosuff in types.PlayPlayer._derived_sums:
                    replace_or(*types.PlayPlayer._derived_sums[nosuff])
            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`, 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.Play, kw)
            _append_conds(self._agg_default_cond, types.PlayPlayer, kw)
            return self

        def as_games(self):
            """
            Executes the query and returns the results as a list of
            `nfldb.Game` objects.
            """
            self._assert_no_aggregate()

            self._sort_tables = [types.Game]
            ids = self._ids('game', self._sorter)
            results = []
            q = 'SELECT %s FROM game %s %s'
            with Tx(self._db) as cursor:
                q = q % (
                    types._select_fields(types.Game),
                    _prefix_and(_sql_pkey_in(cursor, ['gsis_id'], ids['game'])),
                    self._sorter.sql(tabtype=types.Game),
                )
                cursor.execute(q)

                for row in cursor.fetchall():
                    results.append(types.Game.from_row(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()

            self._sort_tables = [types.Drive]
            ids = self._ids('drive', self._sorter)
            tables = self._tables()
            results = []
            q = 'SELECT %s FROM drive %s %s'
            with Tx(self._db) as cursor:
                pkey = _pk_play(cursor, ids, tables=tables)
                q = q % (
                    types._select_fields(types.Drive),
                    _prefix_and(pkey),
                    self._sorter.sql(tabtype=types.Drive),
                )
                cursor.execute(q)

                for row in cursor.fetchall():
                    if (row['gsis_id'], row['drive_id']) in ids['drive']:
                        results.append(types.Drive.from_row(self._db, row))
            return results

        def _as_plays(self):
            """
            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.
            """
            self._assert_no_aggregate()

            plays = OrderedDict()
            ids = self._ids('play', self._sorter)
            pset = _play_set(ids)
            pkey = None
            q = 'SELECT %s FROM play %s %s'

            tables = self._tables()
            tables.add('play')

            with Tx(self._db, factory=tuple_cursor) as cursor:
                pkey = _pk_play(cursor, ids, tables=tables)

                q = q % (
                    types._select_fields(types.Play),
                    _prefix_and(pkey),
                    self._sorter.sql(tabtype=types.Play),
                )
                cursor.execute(q)
                init = types.Play._from_tuple
                for t in cursor.fetchall():
                    pid = (t[0], t[1], t[2])
                    if _in_play_set(pset, pid):
                        p = init(self._db, t)
                        plays[pid] = p
            return plays, pkey

        def as_plays(self, fill=True):
            """
            Executes the query and returns the results as a list of
            `nlfdb.Play` objects with the `nfldb.Play.play_players`
            attribute filled with player statistics.

            If `fill` is `False`, then player statistics will not be added
            to each `nfldb.Play` object returned. This can significantly
            speed things up if you don't need to access player statistics.

            Note that when `fill` is `False`, the `nfldb.Play.play_player`
            attribute is still available, but the data will be retrieved
            on-demand for each play. Also, if `fill` is `False`, then any
            sorting criteria specified to player statistics will be
            ignored.
            """
            self._assert_no_aggregate()

            self._sort_tables = [types.Play, types.PlayPlayer]
            plays, pkey = self._as_plays()
            if not fill:
                return plays.values()

            q = 'SELECT %s FROM play_player %s %s'
            with Tx(self._db, factory=tuple_cursor) as cursor:
                q = q % (
                    types._select_fields(types.PlayPlayer),
                    _prefix_and(pkey),
                    self._sorter.sql(tabtype=types.PlayPlayer),
                )
                cursor.execute(q)
                init = types.PlayPlayer._from_tuple
                for t in cursor.fetchall():
                    pid = (t[0], t[1], t[2])
                    if pid in plays:
                        play = plays[pid]
                        if play._play_players is None:
                            play._play_players = []
                        play._play_players.append(init(self._db, t))
            return self._sorter.sorted(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()

            self._sort_tables = [types.PlayPlayer]
            ids = self._ids('play_player', self._sorter)
            pset = _play_set(ids)
            player_pks = None
            tables = self._tables()
            tables.add('play_player')

            results = []
            q = 'SELECT %s FROM play_player %s %s'
            with Tx(self._db, factory=tuple_cursor) as cursor:
                pkey = _pk_play(cursor, ids, tables=tables)

                # Normally we wouldn't need to add this restriction on players,
                # but the identifiers in `ids` correspond to either plays or
                # players, and not their combination.
                if 'player' in tables or 'play_player':
                    player_pks = _sql_pkey_in(cursor, ['player_id'], ids['player'])

                q = q % (
                    types._select_fields(types.PlayPlayer),
                    _prefix_and(player_pks, pkey),
                    self._sorter.sql(tabtype=types.PlayPlayer),
                )
                cursor.execute(q)
                init = types.PlayPlayer._from_tuple
                for t in cursor.fetchall():
                    pid = (t[0], t[1], t[2])
                    if _in_play_set(pset, pid):
                        results.append(init(self._db, t))
            return results

        def as_players(self):
            """
            Executes the query and returns the results as a list of
            `nfldb.Player` objects.
            """
            self._assert_no_aggregate()

            self._sort_tables = [types.Player]
            ids = self._ids('player', self._sorter)
            results = []
            q = 'SELECT %s FROM player %s %s'
            with Tx(self._db) as cur:
                q = q % (
                    types._select_fields(types.Player),
                    _prefix_and(_sql_pkey_in(cur, ['player_id'], ids['player'])),
                    self._sorter.sql(tabtype=types.Player),
                )
                cur.execute(q)

                for row in cur.fetchall():
                    results.append(types.Player.from_row(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.
            """
            # The central approach here is to buck the trend of the other
            # `as_*` methods and do a JOIN to perform our search.
            # We do this because `IN` expressions are limited in the number
            # of sub-expressions they can contain, and since we can't do our
            # usual post-filtering with Python (since it's an aggregate),
            # we must resort to doing all the filtering in PostgreSQL.
            #
            # The only other option I can think of is to load the identifiers
            # into a temporary table and use a subquery with an `IN` expression,
            # which I'm told isn't subject to the normal limitations. However,
            # I'm not sure if it's economical to run a query against a big
            # table with so many `OR` expressions. More convincingly, the
            # approach I've used below seems to be *fast enough*.
            #
            # Ideas and experiments are welcome. Using a join seems like the
            # most sensible approach at the moment (and it's simple!), but I'd like
            # to experiment with other ideas in the future.
            tables, agg_tables = self._tables(), self._agg_tables()
            gids, player_ids = None, None
            joins = defaultdict(str)
            results = []

            with Tx(self._db) as cur:
                if 'game' in tables:
                    joins['game'] = '''
                        LEFT JOIN game
                        ON play_player.gsis_id = game.gsis_id
                    '''
                if 'drive' in tables:
                    joins['drive'] = '''
                        LEFT JOIN drive
                        ON play_player.gsis_id = drive.gsis_id
                            AND play_player.drive_id = drive.drive_id
                    '''
                if 'play' in tables or 'play' in agg_tables:
                    joins['play'] = '''
                        LEFT JOIN play
                        ON play_player.gsis_id = play.gsis_id
                            AND play_player.drive_id = play.drive_id
                            AND play_player.play_id = play.play_id
                    '''
                if 'player' in tables:
                    joins['player'] = '''
                        LEFT JOIN player
                        ON play_player.player_id = player.player_id
                    '''

                where = self._sql_where(cur, ['game', 'drive', 'play',
                                              'play_player', 'player'])
                having = self._sql_where(cur, ['play', 'play_player'],
                                         prefix='', aggregate=True)
                q = '''
                    SELECT play_player.player_id, {sum_fields}
                    FROM play_player
                    {join_game}
                    {join_drive}
                    {join_play}
                    {join_player}
                    {where}
                    GROUP BY play_player.player_id
                    {having}
                    {order}
                '''.format(
                    sum_fields=types._sum_fields(types.PlayPlayer),
                    join_game=joins['game'], join_drive=joins['drive'],
                    join_play=joins['play'], join_player=joins['player'],
                    where=_prefix_and(player_ids, where, prefix='WHERE '),
                    having=_prefix_and(having, prefix='HAVING '),
                    order=self._sorter.sql(tabtype=types.PlayPlayer, prefix=''),
                )
                cur.execute(q)

                fields = (types._player_categories.keys()
                          + types.PlayPlayer._sql_derived)
                for row in cur.fetchall():
                    stats = {}
                    for f in fields:
                        v = row[f]
                        if v != 0:
                            stats[f] = int(v)
                    pp = types.PlayPlayer(self._db, None, None, None,
                                          row['player_id'], None, stats)
                    results.append(pp)
            return results

        def _tables(self):
            """Returns all the tables referenced in the search criteria."""
            tabs = set()
            for cond in self._andalso + self._orelse:
                tabs = tabs.union(cond._tables())
            return tabs

        def _agg_tables(self):
            """
            Returns all the tables referenced in the aggregate search criteria.
            """
            tabs = set()
            for cond in self._agg_andalso + self._agg_orelse:
                tabs = tabs.union(cond._tables())
            return tabs

        def _sql_where(self, cur, tables, prefix=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 _sql_where(cur, tables, self._agg_andalso, self._agg_orelse,
                                  prefix=prefix, aggregate=aggregate)
            else:
                return _sql_where(cur, tables, self._andalso, self._orelse,
                                  prefix=prefix, aggregate=aggregate)

        def _ids(self, as_table, sorter, tables=None):
            """
            Returns a dictionary of primary keys matching the criteria
            specified in this query for the following tables: game, drive,
            play and player. The returned dictionary will have a key for
            each table with a corresponding `IdSet`, which may be empty
            or full.

            Each `IdSet` contains primary key values for that table. In the
            case of the `drive` and `play` table, those values are tuples.
            """
            # This method is where most of the complexity in this module lives,
            # since it is where most of the performance considerations are made.
            # Namely, the search criteria in `self` are spliced out by table
            # and used to find sets of primary keys for each table. The primary
            # keys are then used to filter subsequent searches on tables.
            #
            # The actual data returned is confined to the identifiers returned
            # from this method.

            # Initialize sets to "full". This distinguishes an empty result
            # set and a lack of search.
            ids = dict([(k, IdSet.full())
                        for k in ('game', 'drive', 'play', 'player')])

            # A list of fields for each table for easier access by table name.
            table_types = {
                'game': types.Game,
                'drive': types.Drive,
                'play': types.Play,
                'play_player': types.PlayPlayer,
                'player': types.Player,
            }

            def merge(add):
                for table, idents in ids.items():
                    ids[table] = idents.intersection(add.get(table, IdSet.full()))

            def osql(table):
                if table == 'play_player' and as_table == 'play':
                    # A special case to handle weird sorting issues since
                    # some tables use the same column names.
                    # When sorting plays, we only want to allow sorting on
                    # player statistical fields and nothing else (like gsis_id,
                    # play_id, etc.).
                    player_stat = False
                    for field, _ in sorter.exprs:
                        is_derived = field in types.PlayPlayer._sql_derived
                        if field in types._player_categories or is_derived:
                            player_stat = True
                            break
                    if not player_stat:
                        return ''
                elif table != as_table:
                    return ''
                return sorter.sql(tabtype=table_types[table], only_limit=True)

            def ids_game(cur):
                game = IdSet.empty()
                cur.execute('''
                    SELECT gsis_id FROM game %s %s
                ''' % (_prefix_and(self._sql_where(cur, ['game'])), osql('game')))

                for row in cur.fetchall():
                    game.add(row[0])
                return {'game': game}

            def ids_drive(cur):
                idexp = pkin(['gsis_id'], ids['game'])
                cur.execute('''
                    SELECT gsis_id, drive_id FROM drive %s %s
                ''' % (_prefix_and(idexp, where('drive')), osql('drive')))

                game, drive = IdSet.empty(), IdSet.empty()
                for row in cur.fetchall():
                    game.add(row[0])
                    drive.add((row[0], row[1]))
                return {'game': game, 'drive': drive}

            def ids_play(cur):
                cur.execute('''
                    SELECT gsis_id, drive_id, play_id FROM play %s %s
                ''' % (_prefix_and(_pk_play(cur, ids), where('play')),
                       osql('play')))
                pset = _play_set(ids)
                game, drive, play = IdSet.empty(), IdSet.empty(), IdSet.empty()
                for row in cur.fetchall():
                    pid = (row[0], row[1], row[2])
                    if not _in_play_set(pset, pid):
                        continue
                    game.add(row[0])
                    drive.add(pid[0:2])
                    play.add(pid)
                return {'game': game, 'drive': drive, 'play': play}

            def ids_play_player(cur):
                cur.execute('''
                    SELECT gsis_id, drive_id, play_id, player_id
                    FROM play_player %s %s
                ''' % (_prefix_and(_pk_play(cur, ids), where('play_player')),
                       osql('play_player')))
                pset = _play_set(ids)
                game, drive, play = IdSet.empty(), IdSet.empty(), IdSet.empty()
                player = IdSet.empty()
                for row in cur.fetchall():
                    pid = (row[0], row[1], row[2])
                    if not _in_play_set(pset, pid):
                        continue
                    game.add(row[0])
                    drive.add(pid[0:2])
                    play.add(pid)
                    player.add(row[3])
                return {'game': game, 'drive': drive, 'play': play,
                        'player': player}

            def ids_player(cur):
                cur.execute('''
                    SELECT player_id FROM player %s %s
                ''' % (_prefix_and(where('player')), osql('player')))
                player = IdSet.empty()
                for row in cur.fetchall():
                    player.add(row[0])

                player_pks = pkin(['player_id'], player)
                cur.execute('''
                    SELECT gsis_id, drive_id, play_id, player_id
                    FROM play_player %s
                ''' % (_prefix_and(_pk_play(cur, ids), player_pks)))

                pset = _play_set(ids)
                game, drive, play = IdSet.empty(), IdSet.empty(), IdSet.empty()
                player = IdSet.empty()
                for row in cur.fetchall():
                    pid = (row[0], row[1], row[2])
                    if not _in_play_set(pset, pid):
                        continue
                    game.add(row[0])
                    drive.add(pid[0:2])
                    play.add(pid)
                    player.add(row[3])
                return {'game': game, 'drive': drive, 'play': play,
                        'player': player}

            with Tx(self._db, factory=tuple_cursor) as cur:
                def pkin(pkeys, ids, prefix=''):
                    return _sql_pkey_in(cur, pkeys, ids, prefix=prefix)

                def where(table):
                    return self._sql_where(cur, [table])

                def should_search(table):
                    tabtype = table_types[table]
                    return where(table) or sorter.is_restraining(tabtype)

                if tables is None:
                    tables = self._tables()

                # Start with games since it has the smallest space.
                if should_search('game'):
                    merge(ids_game(cur))
                if should_search('drive'):
                    merge(ids_drive(cur))
                if should_search('play'):
                    merge(ids_play(cur))
                if should_search('play_player'):
                    merge(ids_play_player(cur))
                if should_search('player') or as_table == 'player':
                    merge(ids_player(cur))
            return ids

#### 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.

Show source.

    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._sort_tables = []
        """The tables to restrain limiting criteria 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`, 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:

    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:

    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.)

Show source.

    def aggregate(self, **kw):
        """
        This is just like `nfldb.Query.play`, 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.Play, kw)
        _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.

Show source.

    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.

Show source.

    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.
        """
        # The central approach here is to buck the trend of the other
        # `as_*` methods and do a JOIN to perform our search.
        # We do this because `IN` expressions are limited in the number
        # of sub-expressions they can contain, and since we can't do our
        # usual post-filtering with Python (since it's an aggregate),
        # we must resort to doing all the filtering in PostgreSQL.
        #
        # The only other option I can think of is to load the identifiers
        # into a temporary table and use a subquery with an `IN` expression,
        # which I'm told isn't subject to the normal limitations. However,
        # I'm not sure if it's economical to run a query against a big
        # table with so many `OR` expressions. More convincingly, the
        # approach I've used below seems to be *fast enough*.
        #
        # Ideas and experiments are welcome. Using a join seems like the
        # most sensible approach at the moment (and it's simple!), but I'd like
        # to experiment with other ideas in the future.
        tables, agg_tables = self._tables(), self._agg_tables()
        gids, player_ids = None, None
        joins = defaultdict(str)
        results = []
        with Tx(self._db) as cur:
            if 'game' in tables:
                joins['game'] = '''
                    LEFT JOIN game
                    ON play_player.gsis_id = game.gsis_id
                '''
            if 'drive' in tables:
                joins['drive'] = '''
                    LEFT JOIN drive
                    ON play_player.gsis_id = drive.gsis_id
                        AND play_player.drive_id = drive.drive_id
                '''
            if 'play' in tables or 'play' in agg_tables:
                joins['play'] = '''
                    LEFT JOIN play
                    ON play_player.gsis_id = play.gsis_id
                        AND play_player.drive_id = play.drive_id
                        AND play_player.play_id = play.play_id
                '''
            if 'player' in tables:
                joins['player'] = '''
                    LEFT JOIN player
                    ON play_player.player_id = player.player_id
                '''
            where = self._sql_where(cur, ['game', 'drive', 'play',
                                          'play_player', 'player'])
            having = self._sql_where(cur, ['play', 'play_player'],
                                     prefix='', aggregate=True)
            q = '''
                SELECT play_player.player_id, {sum_fields}
                FROM play_player
                {join_game}
                {join_drive}
                {join_play}
                {join_player}
                {where}
                GROUP BY play_player.player_id
                {having}
                {order}
            '''.format(
                sum_fields=types._sum_fields(types.PlayPlayer),
                join_game=joins['game'], join_drive=joins['drive'],
                join_play=joins['play'], join_player=joins['player'],
                where=_prefix_and(player_ids, where, prefix='WHERE '),
                having=_prefix_and(having, prefix='HAVING '),
                order=self._sorter.sql(tabtype=types.PlayPlayer, prefix=''),
            )
            cur.execute(q)
            fields = (types._player_categories.keys()
                      + types.PlayPlayer._sql_derived)
            for row in cur.fetchall():
                stats = {}
                for f in fields:
                    v = row[f]
                    if v != 0:
                        stats[f] = int(v)
                pp = types.PlayPlayer(self._db, None, None, None,
                                      row['player_id'], None, stats)
                results.append(pp)
        return results

def as_drives(

self)

Executes the query and returns the results as a list of `Drive` objects.

Show source.

    def as_drives(self):
        """
        Executes the query and returns the results as a list of
        `nfldb.Drive` objects.
        """
        self._assert_no_aggregate()
        self._sort_tables = [types.Drive]
        ids = self._ids('drive', self._sorter)
        tables = self._tables()
        results = []
        q = 'SELECT %s FROM drive %s %s'
        with Tx(self._db) as cursor:
            pkey = _pk_play(cursor, ids, tables=tables)
            q = q % (
                types._select_fields(types.Drive),
                _prefix_and(pkey),
                self._sorter.sql(tabtype=types.Drive),
            )
            cursor.execute(q)
            for row in cursor.fetchall():
                if (row['gsis_id'], row['drive_id']) in ids['drive']:
                    results.append(types.Drive.from_row(self._db, row))
        return results

def as_games(

self)

Executes the query and returns the results as a list of `Game` objects.

Show source.

    def as_games(self):
        """
        Executes the query and returns the results as a list of
        `nfldb.Game` objects.
        """
        self._assert_no_aggregate()
        self._sort_tables = [types.Game]
        ids = self._ids('game', self._sorter)
        results = []
        q = 'SELECT %s FROM game %s %s'
        with Tx(self._db) as cursor:
            q = q % (
                types._select_fields(types.Game),
                _prefix_and(_sql_pkey_in(cursor, ['gsis_id'], ids['game'])),
                self._sorter.sql(tabtype=types.Game),
            )
            cursor.execute(q)
            for row in cursor.fetchall():
                results.append(types.Game.from_row(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.

Show source.

    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()
        self._sort_tables = [types.PlayPlayer]
        ids = self._ids('play_player', self._sorter)
        pset = _play_set(ids)
        player_pks = None
        tables = self._tables()
        tables.add('play_player')
        results = []
        q = 'SELECT %s FROM play_player %s %s'
        with Tx(self._db, factory=tuple_cursor) as cursor:
            pkey = _pk_play(cursor, ids, tables=tables)
            # Normally we wouldn't need to add this restriction on players,
            # but the identifiers in `ids` correspond to either plays or
            # players, and not their combination.
            if 'player' in tables or 'play_player':
                player_pks = _sql_pkey_in(cursor, ['player_id'], ids['player'])
            q = q % (
                types._select_fields(types.PlayPlayer),
                _prefix_and(player_pks, pkey),
                self._sorter.sql(tabtype=types.PlayPlayer),
            )
            cursor.execute(q)
            init = types.PlayPlayer._from_tuple
            for t in cursor.fetchall():
                pid = (t[0], t[1], t[2])
                if _in_play_set(pset, pid):
                    results.append(init(self._db, t))
        return results

def as_players(

self)

Executes the query and returns the results as a list of `Player` objects.

Show source.

    def as_players(self):
        """
        Executes the query and returns the results as a list of
        `nfldb.Player` objects.
        """
        self._assert_no_aggregate()
        self._sort_tables = [types.Player]
        ids = self._ids('player', self._sorter)
        results = []
        q = 'SELECT %s FROM player %s %s'
        with Tx(self._db) as cur:
            q = q % (
                types._select_fields(types.Player),
                _prefix_and(_sql_pkey_in(cur, ['player_id'], ids['player'])),
                self._sorter.sql(tabtype=types.Player),
            )
            cur.execute(q)
            for row in cur.fetchall():
                results.append(types.Player.from_row(self._db, row))
        return results

def as_plays(

self, fill=True)

Executes the query and returns the results as a list of `nlfdb.Play` objects with the `play_players` attribute filled with player statistics.

If `fill` is `False`, then player statistics will not be added to each `Play` object returned. This can significantly speed things up if you don't need to access player statistics.

Note that when `fill` is `False`, the `nfldb.Play.play_player` attribute is still available, but the data will be retrieved on-demand for each play. Also, if `fill` is `False`, then any sorting criteria specified to player statistics will be ignored.

Show source.

    def as_plays(self, fill=True):
        """
        Executes the query and returns the results as a list of
        `nlfdb.Play` objects with the `nfldb.Play.play_players`
        attribute filled with player statistics.
        If `fill` is `False`, then player statistics will not be added
        to each `nfldb.Play` object returned. This can significantly
        speed things up if you don't need to access player statistics.
        Note that when `fill` is `False`, the `nfldb.Play.play_player`
        attribute is still available, but the data will be retrieved
        on-demand for each play. Also, if `fill` is `False`, then any
        sorting criteria specified to player statistics will be
        ignored.
        """
        self._assert_no_aggregate()
        self._sort_tables = [types.Play, types.PlayPlayer]
        plays, pkey = self._as_plays()
        if not fill:
            return plays.values()
        q = 'SELECT %s FROM play_player %s %s'
        with Tx(self._db, factory=tuple_cursor) as cursor:
            q = q % (
                types._select_fields(types.PlayPlayer),
                _prefix_and(pkey),
                self._sorter.sql(tabtype=types.PlayPlayer),
            )
            cursor.execute(q)
            init = types.PlayPlayer._from_tuple
            for t in cursor.fetchall():
                pid = (t[0], t[1], t[2])
                if pid in plays:
                    play = plays[pid]
                    if play._play_players is None:
                        play._play_players = []
                    play._play_players.append(init(self._db, t))
        return self._sorter.sorted(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.

Show source.

    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.

Show source.

    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.
        """
        _append_conds(self._default_cond, types.Game, kw)
        if 'team' in kw:
            ors = {'home_team': kw['team'], 'away_team': kw['team']}
            self.andalso(Query(self._db, orelse=True).game(**ors))
        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.

Show source.

    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 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.

Show source.

    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)
        _append_conds(self._default_cond, types.PlayPlayer, kw)
        # 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(**ors))
            if nosuff in types.PlayPlayer._derived_sums:
                replace_or(*types.PlayPlayer._derived_sums[nosuff])
        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.

Show source.

    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 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:

    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:

    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.)

Show source.

    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.

Show source.

    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.

Show source.

    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. Otherwise, upon exit of the with block, commit is 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:

    with Tx(conn) as cursor:
        ...

Which is meant to be roughly equivalent to the following:

    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.)

Show source.

    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. Otherwise, upon exit of the with
        block, commit is 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 = self.__conn.cursor_factory

        def __enter__(self):
            self.__cursor = self.__conn.cursor(name=self.__name,
                                               cursor_factory=self.__factory)
            return self.__cursor

        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.

Show source.

    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 = self.__conn.cursor_factory

Documentation generated by `pdoc` 0.1.8. pdoc is in the public domain with the UNLICENSE.