Skip to content

3. Design

E. Zach Okruhlik edited this page Jul 21, 2015 · 22 revisions

Single Page Application

Our app is considered a SPA (Single Page Application) which means that once the initial page is loaded, the site never has to refresh when one interacts with it. Architecturally, our front end is completely separated from the back end. The only surface where they touch is when the server sends the initial payload of the index.html page along with all of our assets.

Apiary

Apiary provides a convenient way to mock up and test a RESTful API. Using the in-site API Blueprint Editor, a mock-up can be generated, documented, and shared with team members. Apiary is very friendly for collaborative design, instant API mock, generating documentation, integrating code samples, debugging, and automated testing . . . The API mock allows your users to take your API for a spin without writing any code . . . Apiary provides the ease of Markdown combined with the power of automated mock servers, tests, validations, proxies, and code samples in your language bindings. -Apiary.io

This allowed an easy way for our group to test and document our API. If another user were to need to utilize our API, they can easily access our website's API documentation and acquire the information needed to utilize the API.

Our Restful API

Click here to view our API documentation on Apiary

Our API is designed to correspond to the data models: Player, Team, Game. Each Model is broken down into a collection target and single resource targets.

The collection target allows the client to make HTTP GET requests and query against the entire database for the target model, filtering on any attribute of that model. This allows the client to retrieve any and all data from the database using custom filtered queries, providing dynamic resource retrieval targets. Collection targets also accept POST requests with data models structured as JSON objects, which will add the provided object to the database. In this way the database may be extended via API calls.

The single resource targets provide specific or unique resources to the client. These resources are either inaccessible via the collection target or common enough to warrant a dedicated API. Filtering against single resource targets is not possible, and as such query arguments are not accepted for single resource API targets.

An additional layer of abstraction has been added between the Flask Route targets and the actual route handlers. This has been done in order to maintain separation of concern between the HTTP route end points and the data handlers themselves, such that the handlers may be replaced or the routes may be re-routed without conflict. Also should we need to scale towards asynchronous data requests to avoid blocking on a given route this abstraction would allow for the data handlers to be invoked via some worker thread abstraction (tasklets, or promises etc.) which would ease the load on Flask. This could be further extended by providing a reverse-proxy HTTP server such as NGINX between our Application server (Flask) and the raw HTTP port. This would allow API Requests to be multiplexed to multiple application servers from NGINX and furthermore routes to given application servers could be asynchronously handled when accessing data. While this scaling will not be necessary for this project, putting the infrastructure in place is good practice.

API Endpoints

Player

/resources/players?{args} [GET, POST]

GET: Requests to this API endpoint will filter the players in the database against the arguments provided. If no filter arguments are provided the entire list will be returned as a JSON object.

POST: A post request to the collection target with a JSON player object as the message body will add that object to the database.

/resources/player/{name}?{attribute} [GET]

Requests to this API target will return the player with {name} or 404 if the resources does not exist. If multiple resources are found, they are returned as a list. If the optional attribute query is included, only the attributes requested for the given player {name} will be returned.

Team

/resources/teams?{args} [GET, POST]

GET: This API target will return all teams in the database, filtered by attribute provided as query arguments. If no arguments are provided all team objects will be returned. Integer arguments may be provided as a single value or a range tuple indicated an inclusive range.

POST: A post request to this target with a JSON object as the message body will add that object to the database.

/resources/team/{team_name}?{attribute} [GET]

Requests will return the team object specified by {team_name} or 404 if not found. If the {attribute} argument is provided the specified attribute will be returned singly instead of the entire team object.

/resources/team/{team_name}/schedule [GET]

Requests will return the list of games played by {team_name} during the season.

/resources/team/{team_name}/top_starters [GET]

Requests will return a list of five players for {team_name} who started the most games during the season.

/resources/team/{team_name}/wins [GET]

Requests will return a list of all games won by {team_name} during the season.

/resources/team/{team_name}/losses [GET]

Requests will return a list of all games lost by {team_name} during the season.

Game

/resources/games?{args} [GET, POST]

GET: Collection API endpoint, this target will return game objects stored in the database filtered by the query parameters. If no parameters are provided all games will be returned. Requests may be filtered by any attribute of the Game model, where integer attributes may be provided singly or as an inclusive tuple range value.

POST: A post request with a JSON Game data model object in the message body will add the object to the database.

/resources/game/{id} [GET]

Requests will return the JSON game object associated with game {id}.

/resources/game/{month_id} [GET]

Requests will return a list of all games played during {month_id} where the value corresponds to the numerical month (1-12).

/resources/game/{site} [GET]

Request will return a list of all games played at {site} during the year.

Models

These are the models we are using, with descriptions added for the most important attributes:

Player

Description: This model represents an individual NBA player and includes performance data, media links, and relationships to other models.

Attributes

  • name = db.Column(db.String(256), primary_key=True) - The player's name.
  • picture = db.Column(db.String(256), unique=True) - A link to the player's picture.
  • experience_years = db.Column(db.String(256))
  • draft_info = db.Column(db.String(256))
  • position = db.Column(db.String(256)) - The player's position.
  • player_number = db.Column(db.String(256)) - The player's number.
  • current_team = db.Column(db.String(256)) - The player's team.
  • college = db.Column(db.String(256))
  • birth_info = db.Column(db.String(256))
  • weight = db.Column(db.String(256)) - The player's weight in pounds.
  • twitter = db.Column(db.String(256))
  • age = db.Column(db.String(256)) - The player's age in years.
  • youtube_link_1 = db.Column(db.String(256)) - A YouTube link to a player's highlight reel.
  • youtube_link_2 = db.Column(db.String(256))
  • youtube_link_3 = db.Column(db.String(256))
  • career_GS = db.Column(db.String(256))
  • career_REB = db.Column(db.String(256))
  • career_BLK = db.Column(db.String(256))
  • career_FT_PCT = db.Column(db.String(256))
  • career_DR = db.Column(db.String(256))
  • career_MIN = db.Column(db.String(256))
  • career_FG_PCT = db.Column(db.String(256))
  • career_3PM_A = db.Column(db.String(256))
  • career_OR = db.Column(db.String(256))
  • career_FGM_A = db.Column(db.String(256))
  • career_STL = db.Column(db.String(256))
  • career_TO = db.Column(db.String(256))
  • career_3PM_PCT = db.Column(db.String(256))
  • career_AST = db.Column(db.String(256))
  • career_GP = db.Column(db.String(256))
  • career_PF = db.Column(db.String(256))
  • career_PTS = db.Column(db.String(256))
  • CAREER_FTM_A = db.Column(db.String(256))
  • season_TO = db.Column(db.String(256))
  • season_GS = db.Column(db.String(256))
  • season_FG_PCT = db.Column(db.String(256))
  • seasonseason__PTS = db.Column(db.String(256))
  • season_OR = db.Column(db.String(256))
  • season_GP = db.Column(db.String(256))
  • season_PF = db.Column(db.String(256))
  • season_REB = db.Column(db.String(256))
  • season_FTM_A = db.Column(db.String(256))
  • season_BLK = db.Column(db.String(256))
  • season_MIN = db.Column(db.String(256))
  • season_STL = db.Column(db.String(256))
  • season_AST = db.Column(db.String(256))
  • season_FT_PCT = db.Column(db.String(256))
  • season_FGM_A = db.Column(db.String(256))
  • season_3P_PCT = db.Column(db.String(256))
  • season_DR = db.Column(db.String(256))
  • season_3PM_A = db.Column(db.String(256))
  • id_url = db.Column(db.String(256))
  • team_name = db.Column(db.String(256), db.ForeignKey('team.name'))

Team

Description: This model represents one of 30 NBA teams and includes aggregate season data, media links, and relationships to other models.

Attributes

  • players = db. relationship('Player', backref='team', lazy='dynamic') - The relationship that defines the players on the team.
  • name = db.Column(db.String(256), primary_key=True) - The name of the team.
  • conference = db.Column(db.String(256)) - The conference in which the team plays.
  • division = db.Column(db.String(256)) - The division in which the team plays.
  • site_name = db.Column(db.String(256)) - The name of the team's home arena.
  • city = db.Column(db.String(256)) - The team's home city.
  • state = db.Column(db.String(256)) - The team's home state.
  • mascot = db.Column(db.String(256)) - The name of the team's mascot.
  • twitter = db.Column(db.String(256)) - The team's official Twitter account.

Game

Description: This model represents one game played during the 2014-2015 season and includes game data, media links, and relationships to other models.

Attributes id = db.Column(db.Integer, primary_key=True) - The identifying number of the game.

  • home_team = db.Column(db.String(256)) - The name of the home team.
  • away_team = db.Column(db.String(256)) - The name of the away team.
  • date = db.Column(BIGINT(unsigned=True)) - The date on which the game was played in epoch format.
  • home_score = db.Column(db.String(256)) - The home team's final score.
  • away_score = db.Column(db.String(256)) - The away team's final score.
  • home_box_fgm = db.Column(db.String(256))
  • home_box_fga = db.Column(db.String(256))
  • home_box_fg3m = db.Column(db.String(256))
  • home_box_fg3a = db.Column(db.String(256))
  • home_box_ftm = db.Column(db.String(256))
  • home_box_fta = db.Column(db.String(256))
  • home_box_oreb = db.Column(db.String(256))
  • home_box_dreb = db.Column(db.String(256))
  • home_box_ast = db.Column(db.String(256))
  • home_box_stl = db.Column(db.String(256))
  • home_box_blk = db.Column(db.String(256))
  • home_box_to = db.Column(db.String(256))
  • home_box_pf = db.Column(db.String(256))
  • home_box_pts = db.Column(db.String(256))
  • home_box_plus_minus = db.Column(db.String(256))
  • away_box_fgm = db.Column(db.String(256))
  • away_box_fga = db.Column(db.String(256))
  • away_box_fg3m = db.Column(db.String(256))
  • away_box_fg3a = db.Column(db.String(256))
  • away_box_ftm = db.Column(db.String(256))
  • away_box_fta = db.Column(db.String(256))
  • away_box_oreb = db.Column(db.String(256))
  • away_box_dreb = db.Column(db.String(256))
  • away_box_ast = db.Column(db.String(256))
  • away_box_stl = db.Column(db.String(256))
  • away_box_blk = db.Column(db.String(256))
  • away_box_to = db.Column(db.String(256))
  • away_box_pf = db.Column(db.String(256))
  • away_box_pts = db.Column(db.String(256))
  • away_box_plus_minus = db.Column(db.String(256))

Relationships

Many-to-many between teams and games teams = db.relationship('Team', secondary=teams, backref=db.backref('games', lazy='dynamic'))

Many-to-many between players and games players = db.relationship('Player', secondary=players, backref=db.backref('games', lazy='dynamic'))

Clone this wiki locally