beer-api package

A beer-manager API including user reviews, favorites lists, and glass-styles. Built on Python3.3 with the Flask microframework and SQLAlchemy.

Dependencies: Python3.3, SQLite3. Needed python packages are listed in requirements.txt, pass this to PIP for easy installation.

Installation Instructions

  • Clone the GitHub repository git clone https://github.com/binaryatrocity/beer-api.git
  • Move into the app’s directory cd beer-api
  • Create a Python3 virtual environment pyvenv-3.3 venv
  • Activate the new environment source venv/bin/activate
  • Download easy_install setup file wget https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py
  • Install easy_install into the venv venv/bin/python ez_setup.py
  • Use easy_install to setup PIP venv/local/bin/easy_install pip
  • Let PIP handle requirements file venv/local/bin/pip install -r requirements.txt
  • Cleanup some setup files rm ez_setup.py; rm setuptools*.zip
  • Build SQLite3 database for operation ./run.py –builddb
  • Run API with Flask development server ./run.py

This will run the application with the Flask development server, appropriate for testing. The API will be available externally from http://YOURDOMAIN.tld:5000/beer/api/v0.1/. Additional instructions for letting Apache serve the API are below.

Apache HTTPD WSGI Instructions (Untested with pyvenv3.3)

  • Install mod_wsgi for Apache if needed pacman -S libapache2-mod-wsgi OR apt-get install libapache2-mod-wsgi

    see http://code.google.com/p/modwsgi/wiki/QuickInstallationGuide for other installation methods

  • Create a new Apache VirtualHost for the API reference http://flask.pocoo.org/docs/deploying/mod_wsgi/#configuring-apache for example file

  • Enable the new Apache host sudo a2ensite <virtualHostFilename>

  • Restart Apache httpd to enable new configuration

API Endpoint Routes

app.routes.get_auth_token()[source]

Generates an authentication token for current user. Pass the token as username with an empty password as an alternate means of authentication.

URL: /beer/api/v0.1/token
Method: GET
Query Args: None
Authentication: Password Only
app.routes.list_users()[source]

List all users in the database.

URL: /beer/api/v0.1/users
Method: GET
Query Args: sort_by=<column name> <desc?>
Authentication: None

Examples:

Get list of users

GET http://domain.tld/beer/api/v0.1/users

Sorted by creation date

GET http://domain.tld/beer/api/v0.1/users?sort_by=created_on

Sorted by username descending

GET http://domain.tld/beer/api/v0.1/users?sort_by=username%20desc
app.routes.get_user(id)[source]

Retrieve information about a particular user.

URL: /beer/api/v0.1/users/<user_id>
Method: GET
Query Args: None
Authentication: None

Example:

Get data for user with id# 5

GET http://domain.tld/beer/api/v0.1/users/5
app.routes.get_user_reviews(id)[source]

Return list of reviews authored by a particular user.

URL: /beer/api/v0.1/users/<user_id>/reviews
Method: GET
Query Args: sort_by=<column_name> <desc>
Authentication: None

Example:

Get reviews for user with id# 2

GET http://domain.tld/beer/api/v0.1/users/5/reviews

Get reviews for user with id# 1 sorted by aroma

GET http://domain.tld/beer/api/v0.1/users/5/reviews?sort_by=aroma
app.routes.create_user()[source]

Creates a new user and saves it to the database.

URL: /beer/api/v0.1/users
Method: POST
Query Args: None
Authentication: None
Expected Data: username, password
Optional Data: email

Example:

Create user ‘john’ with password ‘suchsafety’ and email ‘john@inter.net’

POST http://domain.tld/beer/api/v0.1/users
data={"username":"john", "email":"john@inter.net", "password":"suchsafety"}
app.routes.edit_user(id)[source]

Allows editing of a user in the database.

URL: /beer/api/v0.1/users/<user_id>
Method: PUT
Query Args: None
Authentication: Token/Password
Optional Data: email, username, password

Example:

Edit user with id#1, change email to ‘john@newisp.com’

PUT http://domain.tld/beer/api/v0.1/users/1
data={"email":"john@newisp.com"}
app.routes.delete_user(id)[source]

Delete a user from the database.

URL: /beer/api/v0.1/users/<user_id>
Method: DELETE
Query Args: None
Authentication: Token/Password

Example:

Delete user with id# 4

DELETE http://domain.tld/beer/api/v0.1/users/4
app.routes.list_glasses()[source]

List glass types in the database.

URL: /beer/api/v0.1/glasses
Method: GET
Query Args: sort_by=<column name> <desc>
Authentication: None

Example:

List all glass styles in the database

GET http://domain.tld/beer/api/v0.1/glasses

List glass styles by name descending

GET http://domain.tld/beer/api/v0.1/glasses?sort_by=name%20desc
app.routes.get_glass(id)[source]

Get data about a particular glass in the database.

URL: /beer/api/v0.1/glasses/<glass_id>
Method: GET
Query Args: None
Authentication: None

Example:

Get data about glass with id# 3

GET http://domain.tld/beer/api/v0.1/glasses/3
app.routes.create_glass()[source]

Add a new glass type to the database.

URL: /beer/api/v0.1/glasses
Method: POST
Query Args: None
Authentication: Token/Password
Expected Data: name

Example:

Create a glass-type with name ‘tumbler’

POST http://domain.tld/beer/api/v0.1/glasses
data={"name":"tumbler"}
app.routes.edit_glass(id)[source]

Edit the name of a glass-type.

URL: /beer/api/v0.1/glasses/<glass_id>
Method: PUT
Query Args: None
Authentication: Token/Password

Example:

Rename glass with id# 2 to ‘Goblet’

PUT http://domain.tld/beer/api/v0.1/glasses/2
data={"name":"Goblet"}
app.routes.delete_glass(id)[source]

Delete a glass-type from the database.

URL: /beer/api/v0.1/glasses/<glass_id>
Method: DELETE
Query Args: None
Authentication: Token/Password

Example:

Delete glass-type with id# 5

DELETE http://domain.tld/beer/api/v0.1/glasses/5
app.routes.list_beers()[source]

List all of the beers in the database.

URL: /beer/api/v0.1/beers
Method: GET
Query Args: sort_by=<column_name> <desc>
Authentication: None

Example:

List all beers in the system

GET http://domain.tld/beer/api/v0.1/beers

Sort beers by ABV%

GET http://domain.tld/beer/api/v0.1/beers?sort_by=abv

Sort beers by calories descending

GET http://domain.tld/beer/api/v0.1/beers?sort_by=calories%20desc
app.routes.get_beer(id)[source]

Get data about a particular beer.

URL: /beer/api/v0.1/beers/<beer_id>
Method: GET
Query Args: None
Authentication: None

Example:

Return data about beer with id# 5

GET http://domain.tld/beer/api/v0.1/beers/5
app.routes.get_beer_reviews(id)[source]

Return list of reviews about a particular beer.

URL: /beer/api/v0.1/beers/<beer_id>/reviews
Method: GET
Query Args: None
Authentication: None

Example:

Retrieve list of reviews about beer with id# 2

GET http://domain.tld/beer/api/v0.1/beers/2/reviews

Get reviews of beer with id# 4 sorted by taste descending

GET http://domain.tld/beer/api/v0.1/beers/4/reviews?sort_by=taste desc
app.routes.create_beer()[source]

Add a new beer to the database and return the object.

URL: /beer/api/v0.1/beers
Method: POST
Query Args: None
Authentication: Token/Password
Expected Data: name, style, abv
Optional Data: brewer, ibu, calories, brew_location, glass_type

Example:

Create a beer named ‘Mayan Chocolate’ by ‘Mobcraft Beers’, style ‘Chocolate Chili Ale’, abv 6.3%

POST http://domain.tld/beer/api/v0.1/beers
data={"name":"Mayan Chocolate", "style":"Chocolate Chili Ale", "brewer":"Mobcraft", "abv":6.4}

Create a beer named ‘Riverwest Stein’ style ‘Amber Lager’ abv 5.6% linked to glass_type 2

POST http://domain.tld/beer/api/v0.1/beers
data={"name":"Riverwest Stein", "glass_type":"http://domain.tld/beer/api/v0.1/glasses/2", "abv":5.6, "style":"Amber lager"}

Create a beer named ‘African Amber’ style ‘Amber Ale’ abv 4.6% brewer ‘Mac & Jacks’ linked to glass_type 4

POST http://domain.tld/beer/api/v0.1/beers
data={"abv":5.6, "style":"Amber Lager", "glass_type":4, "name":"African Amber"}
app.routes.edit_beer(id)[source]

Edit an existing beer in the database.

URL: /beer/api/v0.1/beers/<beer_id>
Method: PUT
Query Args: None
Authentication: Token/Password
Optional Data: brewer, ibu, calories, brew_location, glass_type, brewer, style, abv

Example:

Change the abv value of beer with id# 23 to 3.4

PUT http://domain.tld/beer/api/v0.1/beers/23
data={"abv":3.4}

Change the style and brew_location of beer with id# 6

PUT http://domain.tld/beer/api/v0.1/beers/6
data={"style":"Pale Ale", "brew_location":"Milwaukee, WI"}
app.routes.delete_beer(id)[source]

Remove a particular beer from the database.

URL: /beer/api/v0.1/beers/<beer_id>
Method: DELETE
Query Args: None
Authentication: Token/Password

Example:

Delete the beer with id# 4

DELETE http://domain.tld/beer/api/v0.1/beers/4
app.routes.list_reviews()[source]

Return a list of all reviews in the database.

URL: /beer/api/v0.1/reviews
Method: GET
Query Args: sort_by=<column_name> <desc>
Authentication: None

Examples:

List all reviews in the system

GET http://domain.tld/beer/api/v0.1/reviews

List reviews sorted by aroma rating

GET http://domain.tld/beer/api/v0.1/reviews?sort_by=aroma
app.routes.get_review(id)[source]

Return data about a specific review.

URL: /beer/api/v0.1/reviews/<review_id>
Method: GET
Query Args: None
Authentication: None

Example:

Get data about review with id# 5

GET http://domain.tld/beer/api/v0.1/reviews/5
app.routes.create_review()[source]

Post a new review to the database. Return the review object.

URL: /beer/api/v0.1/reviews
Method: POST
Query Args: None
Authentication: Token/Password
Expected Data: beer_id, aroma, appearance, taste, palate, bottle_style

Example:

Review beer with id# 4 with scores, 3, 3, 8, 4, 1

POST http://domain.tld/beer/api/v0.1/reviews
data={"beer_id":4, "aroma":3, "appearance":3, "taste":8, "palate":4, "bottle_style":1}

Review beer with id# 2 with scores, 1, 1, 5, 5, 5

POST http://domain.tld/beer/api/v0.1/reviews
data={"aroma":"1", "appearance":"1", "taste":"5", "palate":"5", "bottle_style":"5", "beer_id":"http://domain.tld/beer/api/v0.1/beers/2"}
app.routes.edit_review(id)[source]

Edit an existing review.

URL: /beer/api/v0.1/reviews/<review_id>
Method: PUT
Query Args: None
Authentication: Token/Password
Optional Data: aroma, appearance, taste, palate, bottle_style

Example:

Change the aroma score to ‘5’ on review with id# 3

PUT http://domain.tld/beer/api/v0.1/reviews/3
data={"aroma":5}

Change the scores of review with id# 1

PUT http://domain.tld/beer/api/v0.1/reviews/1
data={"aroma":"5", "appearance":"1", "taste":"3", "palate":"3", "bottle_style":"2"}
app.routes.delete_review(id)[source]

Delete a review from the database.

URL: /beer/api/v0.1/reviews/<review_id>
Method: DELETE
Query Args: None
Authentication: Token/Password

Example:

Delete the review with id# 3

DELETE http://domain.tld/beer/api/v0.1/reviews/3
app.routes.get_user_favorites(id)[source]

Return a list of users favorite beers.

URL: /beer/api/v0.1/users/<user_id>/favorites
Method: GET
Query Args: None
Authentication: None

Example:

Retrieve a list of favorites for user with id# 12

GET http://domain.tld/beer/api/v0.1/users/12/favorites
app.routes.create_user_favorites_list(id)[source]

Create a fresh list of favorite beers for a user.

URL: /beer/api/v0.1/users/<user_id>/favorites
Method: POST
Query Args: None
Authentication: None
Expected Data: beers (list of id’s)

Example:

Create favorites list including beers with id#’s 2, 6, 3, 19 for user with id# 5

POST http://domain.tld/beer/api/v0.1/users/5/favorites
data={"beers":["2", 6, 3, "http://domain.tld/beer/api/v0.1/beers/19"]}
app.routes.edit_user_favorites(id)[source]

Add or remove a particular beer from a users favorites list.

URL: /beer/api/v0.1/users/<user_id>/favorites
Method: PUT
Query Args: None
Authentication: Token/Password
Expected Data: beer, action

Example:

Add beer with id# 8 to user with id# 3

POST http://domain.tld/beer/api/v0.1/users/3/favorites
data={"beer":8, "action":"add"}

Remove beer with id# 2 from user with id# 3

POST http://domain.tld/beer/api/v0.1/users/3/favorites
data={"beer":"http://www.domain.tld/beer/api/v0.1/beers/2", "action":"remove"}

Add a beer with id# 71 to user with id# 12

POST http://domain.tld/beer/api/v0.1/users/12/favorites
data={"beer":71, "action":"add"}
app.routes.delete_users_favorites_list(id)[source]

Delete a users entire favorites list.

URL: /beer/api/v0.1/users/<user_id>/favorites
Method: DELETE
Query Args: None
Authentication: Token/Password

Example:

Delete favorites list for user with id# 4

DELETE http://domain.tld/beer/api/v0.1/users/4/favorites
app.routes.list_all_user_favorites()[source]

List favorites list for each user in database.

URL: /beer/api/v0.1/favorites
METHOD: GET
Query Args: None
Authentication: Token/Password

Example:

Get all favorites-list in system

GET http://domain.tld/beer/api/v0.1/favorites
app.routes.before_request()[source]

Checks for Content-Type: application/json on all POST/PUT/DELETE routes.

app.routes.after_request(response)[source]

Update a users last_activity field after each authenticated api request.

app.routes.malformed_error(error)[source]

Returns a 400 error when recieving a malformed request. Adds any messages that have been flash()’ed.

app.routes.not_found_error(error)[source]

Return a 404 error, usually when <int:id> in the route is not a valid id# for that model.

app.routes.method_unallowed_error(error)[source]

Return a 405 error when an unsupported HTTP method is used on an endpoint.

app.routes.internal_error(error)[source]

Return a 500 error when something goes terribly wrong.

app.routes.unauthorized_error()[source]

Returns a 403 error when attempting to access data without authorization.

Database Models

app.models.is_model_id_or_uri(session, model, data)[source]

Return a valid model.primary_key or None.

Keyword arguments:

session – the SQLAlchemy session
model – the db.Model class to lookup
data – the data to parse (expecting an int(id) or an api link

If data is a digit, lookup that primary_key for model, otherwise try to parse the last chunk from an api ‘link’ uri.

class app.models.User(username, email, password)[source]

Bases: flask_sqlalchemy.Model

Database model representing a single User.

Properties:

username – the users name/nickname.
email – the users email address (unused).
password – the users password hash.
created_on – datetime from moment of creation.
last_activity – datetime from last authenticated api call.
last_beer_added – datetime from last added beer (for 24 hour limit).
reviews – reviews writen by the User.
favorites – list of User’s favorite beers.
serialize()[source]

Return a JSON representation of a User object.

add_to_favorites(beer)[source]

Add a beer to users favorites list, checks for redundancy.

remove_from_favorites(beer)[source]

Remove a beer from users favorites list.

static check_auth_token(token)[source]

Validates a user’s authentication token, checks for expiration.

generate_auth_token(expiration=1200)[source]

Generates a new authentication token for a user.

hash_password(password)[source]

Creates a password hash from the plaintext.

check_password(password)[source]

Confirms/validates a plaintext password against the users stored hash

class app.models.Glass(name)[source]

Bases: flask_sqlalchemy.Model

Database model representing a style of beer Glass.

Properties:

name – The name of the glass-style (e.g. ‘Tumbler’)
beers – List of beers that should be served in said glass-type
serialize()[source]

Return a JSON representation of a Glass object.

classmethod id_or_uri_check(data)[source]

Returns a valid primary_key parsed from ‘data’, or None.

class app.models.Beer(name, brewer, ibu, calories, abv, style, brew_location)[source]

Bases: flask_sqlalchemy.Model

Database model representing an individual Beer.

serialize()[source]

Return a JSON representation of a Beer object.

classmethod id_or_uri_check(data)[source]

Returns a valid primary_key parsed from ‘data’, or None.

average_scores[source]

Finds other reviews for the same beer_id, and returns the average of their scores.

class app.models.Review(beer_id, author_id, data)[source]

Bases: flask_sqlalchemy.Model

Database model representing a beer Review.

Properties:

aroma – Score category, 1-5
appearance – Score category, 1-5
taste – Score category, 1-10
palate – Score category, 1-5
bottle_style – Score category, 1-5
serialize()[source]

Return a JSON representation of a Review object.

update_score_values(data)[source]

Updates a Review’s scores based on a passed in dictionary.

classmethod validate_score_values(data)[source]

Checks that a score-dictionary’s values are within review-category constraints.

overall[source]

Returns the sum of all the Reviews categories.