Although i’m still pondering the absolute direction of the project, I still felt it necessary to gather a database of around 100 gin cocktail recipes to get the ball rolling. I now know that I want to focus on the representation of flavour data taking into consideration the paradigm of computer entities, how we program computers to represent nature around us, using Gin as the carrier for that. It has such a botanical flavour yet is subtle enough to mix, and the current Gin Trend possibly has a lot to say about our current economy and affinity towards the crafter, maker attitude. Unlike previous Gin crazes, it seems to be coming out of prosperity with quality product, so that will be interesting. It feels good to have focused a little.

As previously mentioned, I used the Yummly API to gather recipes. This will be relatively brief, as I don’t feel it necessary to this blog to write a comprehensive account, but the full code is available on GitHub.

Yummly API

I used the Yummly developer API for this. I had built a small Flask application, which is basically Python running on Apache. Initially, I used SQLite as my database, but soon after moved to MySQL as SQLite does not have a ‘drop column’ constraint and that proved problematic in versioning the database. Using Alembic its pretty easy to version your database, so that each time you change the structure, you do not have to truncate the data and start again, so this workflow worked well for me.

I used the Yummly.py Python library to create a client and connect to the Yummly API and search specifically for gin cocktail recipes. This can be further specialised using parameters to define the flavours, occasion, omit ingredients, star rating and such, but for now I decided to gather a maximum of 50 basic recipes per call.


def connectClient(aid, key, query, session):
client = Client(api_id=aid, api_key=key, timeout=200, retries=2)
params = {
'q': query,
'start': 0,
'maxResult': 50,
'requirePicutres': True,
'course' : 'Cocktails',
'allowedIngredient[]': [query],
'maxTotalTimeInSeconds': 3600,

}

searchRetrieve(client, params, session)

Within the searchRetrieve callback function, I iterated over each recipe to parse it and add it to the database.


def searchRetrieve(client, params, session):
search = client.search(**params)
flavours = []

for match in search.matches:
recipe = client.recipe(match.id)
recipe_id = recipe.id

And so forth. Above, you can see that the API returns the data as a JSON object, so accessing the data was simple, and here I am simply assigning it to variables for later processing.

The Models

As mentioned previously, I submitted the data into a MySQL database, here using Object Relational Mapping, which I found extremely successful – here, defining the relationships between databases and defining the data types is visually more practical, and integrates with Flask and Alembic to create an efficient system that is is easy to update and debug. Also, passing data as objects works really well within Python/Flask – in general, the interpretation of chunks of information just seems more natural, hence why I have grown to love OOP. Anyway, I digress –

class Recipe(Base):
""""""
__tablename__ = "recipes"

id = Column(Integer, primary_key=True, unique=True)
name = Column(String(64), index=True, unique=True)
url = Column(String(64), index=True)
length = Column(String(64), index=True)
ingredients = relationship('Ingredient', backref='recipes', lazy='dynamic')
flavours = relationship('Flavour', backref='recipes', lazy='dynamic')

def __init__(self, name, url, length):
""""""
self.name = name
self.url = url
self.length = length

def __repr__(self):
return '' % (self.name)

class Ingredient(Base):
""""""
__tablename__ = "ingredients"

id = Column(Integer, primary_key=True)
name = Column(Unicode(64))
quantity = Column(String(64))
unit = Column(String(64))
recipe_id = Column(Integer, ForeignKey('recipes.id'))

def __init__(self, name, quantity, unit):
""""""
self.name = name
self.quantity = quantity
self.unit = unit

def __repr__(self):
return '' % (self.name)

class Flavour(Base):
""""""
__tablename__ = "flavours"

id = Column(Integer, primary_key=True)
recipe_id = Column(Integer, ForeignKey('recipes.id'))
piquant = Column(String(200), index=True)
sour = Column(String(200), index=True)
salty = Column(String(200), index=True)
sweet = Column(String(200), index=True)
bitter = Column(String(200), index=True)
meaty = Column(String(200), index=True)

def __init__(self, piquant, sour, salty, sweet, bitter, meaty):
""""""
self.piquant = piquant
self.sour = sour
self.salty = salty
self.sweet = sweet
self.bitter = bitter
self.meaty = meaty

def __repr__(self):
return '' % (self.recipe_id)

(Full code here) These models work with simple One-To-Many relationships, which although is quite efficient, it isn’t nearly practical enough for this data. For example, one recipe can have many ingredients, but one ingredient can only belong to one recipe, causing duplicate ingredients in the Ingredient table. This is something i’m looking to solve using Many-To-Many relationships in the future, but this worked well for now.

Parsing the Data
Using this particular type of call, ingredients are retrieved as full strings, so for instance “2 oz Gin”. In order for future iterations of this website to be successful and for the success of the ‘Ingredients’ table, it was imperative for the measurement, unit and ingredient name to be separate. I found a snippet of code on GitHub tailored towards this, although it needed a little tweaking to be perfect for my requirements, the snippet can be found here. The splitIngredients function returns a list containing the separated ingredient name, quantity and unit of each Ingredient string that is passed to it.

Submitting to the database
Back within the searchRetrieve function, the ingredients returned by the ingredientsSplit function are retrieved and submitted alongside the other recipe data into each respective model/class:

new_recipe = Recipe(recipe_name, recipe_source_url, recipe_ingredients_length)
new_recipe.flavours = [Flavour(flavors['piquant'], flavors['sour'], flavors['salty'], flavors['sweet'], flavors['bitter'], flavors['meaty'])]

ingredientsSplit = utils.splitIngredients(recipe_ingredients_str)

for ing in ingredientsSplit:
add_ingredient = [Ingredient(ing['name'], ing['quantity'], ing['unit'])]
new_recipe.ingredients.extend(add_ingredient)

try:
session.add(new_recipe)
session.commit()
except (exc.SQLAlchemyError, exc.InvalidRequestError, TypeError):
pass

The full script for this can be found here. Running yumresults.py in my terminal thus executes the script and the recipes are submitted into the database.Screenshot 2015-03-03 12.20.42

Screenshot 2015-03-03 12.20.14

Screenshot 2015-03-03 12.19.21

Displaying the Results
Flask routes Python to the browser for you with so much flexibility, generally the view/route function returns the rendering of a template to populate with model data, but also JSON can be returned, or the page can be redirected to another template or view function. Luckily, all I needed to do was query my Recipe database and retrieve the records as an array to be passed into my template. Note how I only needed to query the Recipe table, as opposed to Recipe, Ingredient and Flavour, oh the beauty of ORM.

@main.route('/playground/jan/10/all')
def index100115():

recipes = Recipe.query.all()
title = "Ginfographic"

return render_template('100115.html', recipes=recipes, title=title)

(Full code, scroll to line 272). Previously in this py file, I imported each table as a model, and the database engine is already bound to the script. All of the recipes are stored as an array/list named recipes, which is then passed to the template. For the template itself, displaying the recipes in plain HTML was a breeze with Jinja2.

100115.html

    {% for recipe in recipes %}

  • {{ recipe.name }}
    • Url : {{recipe.url}}
    • Ingredients :
      • {% for ingredient in recipe.ingredients %}

        {% endfor %}

        Name Quantity Unit
        {{ingredient.name}} {{ingredient.quantity}} {{ingredient.unit}}
    • Flavours :
      • {% for flavour in recipe.flavours %}

        {% endfor %}

        Piquant Sour Salty Sweet Sour Meaty
        {{flavour.piquant}} {{flavour.sour}} {{flavour.salty}} {{flavour.sweet}} {{flavour.sour}} {{flavour.meaty}}

    {% endfor %}

Jinja2 enables the developer to iterate through lists passed to the template, and access each object using dot notation. From simply having access to a Recipe object, thanks to the relationships I defined earlier, the subsequent flavours and ingredients can be accessed and displayed. HTML Tables can be horrible and I totally used a generator for this one, but it displays the results quite well.

This process forms the basis of my whole project, as I intend to keep the data structures (tweaking them to accommodate Many-To-Many relationships) and have collected some great Regex to parse data in the future. Also, I have a login system running for later user submissions, I feel this will save quite a lot of time in the future, built from Miguel Grinberg’s Flasky app. I also noticed there’s really great documentation on releasing an API through Flask, so that is another consideration for the future.