This notebook is meant to demonstrate the transformation of an annotated notebook into a HTTP API using the Jupyter kernel gateway. The result is a simple scotch recommendation engine.

The original scotch data is from https://www.mathstat.strath.ac.uk/outreach/nessie/nessie_whisky.html.


In [ ]:
import JSON
using Requests
import Requests: get

Data

We read the scotch data from a public Dropbox URL to make this notebook more portable. This is acceptable for small, public, demo data which is what we have here.


In [ ]:
scotch_json_response = get("https://dl.dropboxusercontent.com/u/19043899/whisky_features_df.json")
scotch_json = JSON.parse(IOBuffer(scotch_json_response.data))
scotch_similarity_json_reponse = get("https://dl.dropboxusercontent.com/u/19043899/whisky_similarity_features_df.json")
scotch_similarity_json = JSON.parse(IOBuffer(scotch_similarity_json_reponse.data))

In [ ]:
scotch_matrix = Any[]
for i in scotch_json
    temp = Any[]
    for j in values(i)
        push!(temp, j)
    end
    push!(scotch_matrix, temp)
end
scotch_matrix = reshape(scotch_matrix, 86,1)

API

We need to define a global REQUEST JSON string that will be replaced on each invocation of the API. We only care about path parameters and query string arguments, so we default those to blank here for development.


In [ ]:
REQUEST = ""

Provide a way to get the names of all the scotches known by the model.


In [ ]:
# GET /scotches
scotches = String[]
for i in scotch_matrix
    push!(scotches, i[1])
end
println(JSON.json(scotches))
get_scotch_feautre_tuple:

Input = scotch_requested: String
Output = Tuple of (scotch_requested feature names array, scotch_requested feature values array)


In [ ]:
function get_scotch_feature_tuple(scotch_requested)
    #filter scotch matrix to the row where scotch_requested equals the first value/Distillery
    scotch_request_values = filter(row -> row[1] == scotch_requested, scotch_matrix)[1]
    scotch_request_keys = Any[]
    #create array of scotch feature names as the keys
    for i in scotch_json[1]
        push!(scotch_request_keys, i[1])
    end
    return (scotch_request_keys, scotch_request_values)
end

Let clients query for features about a specific scotch given its name.


In [ ]:
# GET /scotches/:scotch
#REQUEST = "{\"args\": {}, \"body\": \"\", \"path\": {\"scotch\": \"AnCnoc\"}}"
request_json = JSON.parse(REQUEST)
scotch_requested = "Talisker"
if haskey(request_json, "path") && haskey(request_json["path"], "scotch")
    scotch_requested = request_json["path"]["scotch"]
end
scotch_requested_tuple = get_scotch_feature_tuple(scotch_requested)
scotch_requested_features = Dict(zip(scotch_requested_tuple[1], scotch_requested_tuple[2]))
println(JSON.json(scotch_requested_features))

Let clients request a set of scotches similar to the one named. Let clients specify how many results they wish to receive (count) and if they want all of the raw feature data included in the result or not (include_features).


In [ ]:
# GET /scotches/:scotch/similar
#REQUEST = "{\"args\": {\"count\": 4, \"include_features\": \"True\"}, \"body\": \"\", \"path\": {\"scotch\": \"AnCnoc\"}}"
request_json = JSON.parse(REQUEST)
scotch_requested = "Talisker"
num_results = 5
features_included = false

if haskey(request_json, "path") && haskey(request_json["path"], "scotch")
    scotch_requested = request_json["path"]["scotch"]
end
if haskey(request_json, "args")
    if haskey(request_json["args"], "count")
        num_results = request_json["args"]["count"]
    end
    if haskey(request_json["args"], "include_features")
        features_included = "True" == request_json["args"]["include_features"]
    end
end     

scotch_request_row = filter(row -> row["Distillery"] == scotch_requested, scotch_similarity_json)[1]
scotch_request_types = String[]
scotch_request_similarities = Float64[]
for (key, value) in scotch_request_row
    if key == "Distillery"
        continue
    end
    push!(scotch_request_types, key)
    push!(scotch_request_similarities, value)
end

#sort array of tuples by scotch similarity values
top_similiar_scotches = sort(collect(zip(scotch_request_similarities, scotch_request_types)), rev=true)
#subset to the top requested results
top_similiar_scotches = top_similiar_scotches[2:2+num_results-1]
#swap the ordering of the tuples in the array of tuples
top_similiar_scotches = map( x -> (x[2],x[1]), top_similiar_scotches)

if features_included
    #for each of the top similiar scotches
    for (distillery, similarity)  in top_similiar_scotches
        #get features of this particular scotch a tuple of arrays
        #where tuple[1] is feature name array, tuple[2] is feature value array
        scotch_feature_tuple = get_scotch_feature_tuple(distillery)
        push!(scotch_feature_tuple[1], "Similarity")
        push!(scotch_feature_tuple[2], float(similarity))
        #zip the feature arrays and convert to a Dict, so as to be converted to JSON easily
        similiar_scotches_with_features = Dict(zip(scotch_feature_tuple[1], scotch_feature_tuple[2]))
        println(JSON.json(similiar_scotches_with_features))
    end
else
    #convert array of tuples to an array of dicts where each value has a map of feature name and feature value
    println(JSON.json(map(x -> ["Distillery" => x[1], "Similarity" => x[2]], top_similiar_scotches)))
end