Model for API

We will begin our journey into APIs by creating and thinking about data. We have learned about Python Lists and dictionaries. In this data example, we are going to make "the best computer jokes ever ;)" and serve them over the Internet. The ultimate objective is to allow our viewers to provide a like or dislike on each of our jokes.

  • This code planning begins by coming up with some jokes and defining a data "model" to keep and manage the jokes.

    • jokes_data contains a list of dictionary records containing joke and reactions:haha or boohoo - joke_list contains collection of jokes we will put into jokes_data
  • Next comes some functions to interact with our jokes

    • def initJokes(): initializes jokes_data
    • def getJokes(): returns the complete list of jokes
    • def getJoke(): returns a single joke from our list
    • ... many more function can be examined by reading comments below ...
import random # the jokes data is on the back end (in the AWS server)

car_jokes_data = []
car_joke_list = [
    "1. What do you call a Ford Fiesta that ran out of gas? A Ford Siesta.",
    "2. When is a car not a car? When it turns into a driveway.",
    "3. What kind of car does yoda drive? A toyoda.",
    "4. Who can drive all their customers away and still make money? Taxi drivers.",
    "5. What kind of car does a snake drive? An Ana-Honda.",
    "6. What has four wheels and flies? A garbage truck.",
    "7. What do you get when dinosaurs crash their cars? Tyrannosaurus wrecks.",
    "8. What snakes are found on cars? Windshield vipers.",
    "9. I couldn’t work out how to fasten my seatbelt. Then it clicked.",
    "10. I ran my Subaru into a lake. Now it’s a Scubaru."
    "11. Why can’t motorcycles hold themselves up? Because they are two-tired.",
    "12. What do you say to a frog who needs a ride? Hop in.",
    "13. What did the tornado say to the sports car? Want to go for a spin.",
    "14. What do you call a used car salesman? A car-deal-ologist.",
    "15. Why are pigs bad drivers? They hog the road.",
    "16. Where do dogs park their cars? In the barking lot.",
    "17. What's a car's favorite meal? Brake-fast.",
    "18. Why do chicken coops have only two doors? If they had four, they would be chicken sedans.",
]

# Initialize jokes
# setting up a dictionary to store all the jokes data and how many likes/dislikes each joke gets
def initJokes():
    # setup jokes into a dictionary with id, joke, haha, boohoo in a FOR LOOP
    item_id = 0
    for item in joke_list:
        car_jokes_data.append({"id": item_id, "joke": item, "haha": 0, "boohoo": 0})
        item_id += 1
    # prime some haha responses
    for i in range(200):
        id = getRandomJoke()['id']
        addJokeHaHa(id)
    # prime some haha responses
    for i in range(50):
        id = getRandomJoke()['id']
        addJokeBooHoo(id)

# jokes are being built into a list
        
# Return all jokes from jokes_data
def getJokes():
    return(car_jokes_data)

# Joke getter
def getJoke(id):
    return(car_jokes_data[id])

# Return random joke from jokes_data
def getRandomJoke():
    return(random.choice(car_jokes_data))

# Liked joke
def favoriteJoke():
    best = 0
    bestID = -1
    for joke in getJokes():
        if joke['haha'] > best:
            best = joke['haha']
            bestID = joke['id']
    return car_jokes_data[bestID]
    
# Jeered joke
def jeeredJoke():
    worst = 0
    worstID = -1
    for joke in getJokes():
        if joke['boohoo'] > worst:
            worst = joke['boohoo']
            worstID = joke['id']
    return car_jokes_data[worstID]

# Add to haha for requested id
def addJokeHaHa(id):
    car_jokes_data[id]['haha'] = car_jokes_data[id]['haha'] + 1
    return car_jokes_data[id]['haha']

# Add to boohoo for requested id
def addJokeBooHoo(id):
    car_jokes_data[id]['boohoo'] = car_jokes_data[id]['boohoo'] + 1
    return car_jokes_data[id]['boohoo']

# Pretty Print joke
def printJoke(joke):
    print(joke['id'], joke['joke'], "\n", "haha:", joke['haha'], "\n", "boohoo:", joke['boohoo'], "\n")

# Number of jokes
def countJokes():
    return len(car_jokes_data)

# Test Joke Model
if __name__ == "__main__": 
    initJokes()  # initialize jokes
    
    # Most likes and most jeered
    best = favoriteJoke()
    print("Most liked", best['haha'])
    printJoke(best)
    worst = jeeredJoke()
    print("Most jeered", worst['boohoo'])
    printJoke(worst)
    
    # Random joke
    print("Random joke")
    printJoke(getRandomJoke()) # have to get jokes out of the back end
    
    # Count of Jokes
    print("Jokes Count: " + str(countJokes())) # we have to store data -- use "str" to add to the counter
Most liked 18
13 15. Why are pigs bad drivers? They hog the road. 
 haha: 18 
 boohoo: 4 

Most jeered 6
11 13. What did the tornado say to the sports car? Want to go for a spin. 
 haha: 12 
 boohoo: 6 

Random joke
4 5. What kind of car does a snake drive? An Ana-Honda 
 haha: 11 
 boohoo: 4 

Jokes Count: 17

Backend Interface for Web API (Control)

An application programming interface (API) is the medium by which different systems of software interact. In our applications we have two big systems:1. Python Backend that stores data beyond a single Web page2. GH Pages/Fastpages Frontend that is responsible for presenting data

To communicate data between Frontend and Backend, this section Backend code provides and interface to the Frontend using a Web Service Endpoint. Examples of endpoints are listed below and can be typed within a browser, which will return JSON data:

As you can see, these Endpoints return JSON. They are NOT that readable by normal humans. However, they are very effective in passing requested data across the Internet. The Frontend code is responsible for formatting and presenting and interface that allows the typical computer user to interact with this data.

The next cell of code is Creating Endpoints that return JSON. This allows developers in the Frontend to interact with Backend data. API is a contract between the Frontend and Backend on how to share data.

FYI, there is NO output from this section .

How to Make an API

from flask import Blueprint, jsonify  # jsonify creates an endpoint response object
from flask_restful import Api, Resource # used for REST API building
import requests  # used for testing 
import random

# Blueprints allow this code to be procedurally abstracted from main.py, meaning code is not all in one place
app_api = Blueprint('api', __name__,
                   url_prefix='/api/jokes')  # endpoint prefix avoid redundantly typing /api/jokes over and over

# API generator https://flask-restful.readthedocs.io/en/latest/api.html#id1 --> this URL calls the API from the backend
api = Api(app_api)

class JokesAPI:
    # not implemented, this would be where we would allow creation of a new Joke
    class _Create(Resource):
        def post(self, joke):
            pass
            
    # getJokes()
    class _Read(Resource):
        def get(self):
            return jsonify(getJokes())

    # getJoke(id)
    class _ReadID(Resource):
        def get(self, id):
            return jsonify(getJoke(id))

    # getRandomJoke()
    class _ReadRandom(Resource):
        def get(self):
            return jsonify(getRandomJoke())
    
    # getRandomJoke()
    class _ReadCount(Resource):
        def get(self):
            count = countJokes()
            countMsg = {'count': count}
            return jsonify(countMsg)

    # put method: addJokeHaHa
    class _UpdateLike(Resource):
        def put(self, id):
            addJokeHaHa(id)
            return jsonify(getJoke(id))

    # put method: addJokeBooHoo
    class _UpdateJeer(Resource):
        def put(self, id):
            addJokeBooHoo(id)
            return jsonify(getJoke(id))

    # building RESTapi interfaces, these routes are added to Web Server http://<server</api/jokes
    # BASICALLY THE STEPS TO MAKE AN API
    api.add_resource(_Create, '/create/<string:joke>')
    api.add_resource(_Read, '/')    # default, which returns all jokes
    api.add_resource(_ReadID, '/<int:id>')
    api.add_resource(_ReadRandom, '/random')
    api.add_resource(_ReadCount, '/count')
    api.add_resource(_UpdateLike, '/like/<int:id>/')
    api.add_resource(_UpdateJeer, '/jeer/<int:id>/')
    

Frontend (View Simulation) and Hacks

This python codes tests endpoints on a server. This can be handy for development and testing when making modifications to the Jokes Web APIs. This code works off of the server endpoint/url, not from code cells above it in this notebook.

To work with this code and make observation for learning...

  • Run a local server from flask_portfolio project and the change server variable to be local
  • Observe the requests endpoints and the output, see if you can observe what is happening/changing on put requests
  • The "requests" are captured into a List, the List is used in the for loop to extract from RESTful API format.
  • Try running this with Debugging and observe what data is being created at each step (Required)
  • Try to format this data in Python print statements to be more readable (Required)
  • Start and stop local server and observe errors
# server = "http://127.0.0.1:5000/" # run local
server = 'https://flask.nighthawkcodingsociety.com/' # run from web server
url = server + "api/jokes/"
responses = []  # responses list

# Get the count of jokes on server
count_response = requests.get(url+"count")
count_json = count_response.json()
count = count_json['count']

# Update likes/dislikes test sequence
num = str(random.randint(0, count-1)) # test a random record
responses.append(
    requests.get(url+num)  # Get/read joke by id
    ) 
responses.append(
    requests.put(url+"like/"+num) # Put/add to like count
    ) 
responses.append(
    requests.put(url+"jeer/"+num) # Put/add to jeer count
    ) 

# Get a random joke
responses.append(
    requests.get(url+"random")  # Get/read a random joke
    ) 

# Cycle through and print responses
for response in responses:
    print(response)
    try:
        print(response.json())
    except:
        print("data error")
<Response [200]>
{'boohoo': 5489, 'haha': 30400, 'id': 1, 'joke': 'Q: Why did I divide sin by tan? A: Just cos.'}
<Response [200]>
{'boohoo': 5489, 'haha': 30401, 'id': 1, 'joke': 'Q: Why did I divide sin by tan? A: Just cos.'}
<Response [200]>
{'boohoo': 5490, 'haha': 30401, 'id': 1, 'joke': 'Q: Why did I divide sin by tan? A: Just cos.'}
<Response [200]>
{'boohoo': 1319, 'haha': 1780, 'id': 11, 'joke': "If it wasn't for C, we’d all be programming in BASI and OBOL."}