Information engineering

Logo

Documentation, backgrounders and tutorial material related to information design, engineering, semantics, ontologies, and vocabularies

Standards and namespaces
Controlled vocabularies
Semantic Web Tools
Learning resources

Tutorial: Introduction to LD APIs Part 2

A tutorial to introduce LD APIs using pyldapi.

In this tutorial, we will learn to:

  1. Extend an existing pyldapi implementation
  2. Add a new LD API

1. Learning Objectives

Attendees will:

2. Pre-requisites and assumptions

We will use the /example-code/pyldapi directory as a starting point and a template for an implementation of a pyLDAPI service.

3. Introducing the Pet LD API

See presentation slides for overview of the Pet LD API.

3.1. Basic layout of a pyLDAPI implementation

The recommended layout of a pyLDAPI implementation is using the Model-View-Controller pattern and creating directories to suit. See below:

/
-- model/
    -- pet.py
-- view/
    -- static/
        ...
    -- templates/
        ...
        page_pet.html        
-- controller/
    -- classes.py
    -- pages.py
-- app.py
-- _config.py
...

Let’s run the example pyLDAPI. If you haven’t run the code yet, run the following

$ virtualenv venv
$ source venv/bin/activate
# Or on a windows bash client
$ source venv/Scripts/activate
$ pip install -r requirements.txt
$ export FLASK_APP=app.py
$ export FLASK_ENV=development
$ flask run --port=3000 --host=0.0.0.0

3.2 Pets Register

Let’s look at the Pets register at http://localhost:3000/pets/.

We can see the pets listed in the register. The Pet Register functionality is defined in the code base in the following file: /controller/classes.py (just glance at it - we’ll look at it in more detail later).

3.3. Pets Viewer

Let’s click on one of the Pets - Rex, and take a look at the view of a Pet instance.

We can see a basic landing page for the Pet Rex.

The information used to populat the Pet instance view comes from:

The Jinja template is used to render the view of the information for Rex. THis is located at:

Exercise 1: Dive into the MVC framework for the Pet register

Take a closer look at these files and draw a map of how the files are connected to render the Pet view.

Exercise 2. Add a new pet

Now let’s add a new pet.

Which file and where in the file would we add the required information?

What do you notice about the information model?

Exercise 3. Let’s add a new Pet view

Based on the template that exists, develop some code to add a new Pet view.

Summary

We have covered how the Pet section of the Example pyLDAPI works using a very basic (JSON) dataset and how to extend it for different LD Views.

4. Adding a Pizza registry based on DBPedia resources

In this next part of the tutorial, we will work on adding a new Register - a Pizza register. We will reuse definitions from DBPedia and expose them as Linked Data resources.

4.1. Explore DBPedia and query Pizza resources

Go to https://dbpedia.org/sparql and put in the SPARQL query text shown below in the Query editor. This will query all Pizza types and their label.

PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX dbo: <http://dbpedia.org/ontology/>
select distinct ?p ?label 
where {
    ?p dbo:type <http://dbpedia.org/resource/Pizza> .
    ?p rdfs:label ?label .
    FILTER(LANG(?label) = "" || LANGMATCHES(LANG(?label), "en")) 
} LIMIT 100

We can use this query as the basis for the Pizza Register we will now implement.

4.2. Add the Pizza register

To implement the Pizza Register, we need to:

4.2.1. Add a route for the Pizza Register

from SPARQLWrapper import SPARQLWrapper, JSON

@classes.route('/pizza/')
def pizza():
    """
    The Register of Pizzas
    :return: HTTP Response
    """
    # prepare items for the ContainerRenderer response instance
    no_of_items = 0
    try:
        page = request.values.get('page') if request.values.get('page') is not None else 1
        per_page = request.values.get('per_page') if request.values.get('per_page') is not None else 20
        items = _get_pizza_items(page, per_page)
        no_of_items = len(items)
    except Exception as e:
        print(e)
        return Response('The Pizza Register is offline', mimetype='text/plain', status=500)    
    
    # create the ContainerRenderer response instance
    r = pyldapi.ContainerRenderer(
        request,
        request.url,
        'Pizza Register',
        'A register of Pizza',
        "http://example.org/def/Pizza",
        "Pizza",
        items,
        no_of_items
    )
    return r.render()

pyLDAPI provides the Register function via ContainerRenderer. ContainerRenderer expects these inputs:

4.2.2. Add a function that will fetch the items for the response

We will now implement the _get_pizza_items() function from the above and using the . The SPARQL Endpoint is located at http://dbpedia.org/sparql.

The SPARQLWrapper library is used to issue the remote queries in python.

def _get_pizza_items(page, per_page):
    sparql = SPARQLWrapper("http://dbpedia.org/sparql")
    sparql.setQuery("""
        PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
        PREFIX dbo: <http://dbpedia.org/ontology/>
        select distinct ?p ?label 
        where {
            ?p dbo:type <http://dbpedia.org/resource/Pizza> .
            ?p rdfs:label ?label .
            FILTER(LANG(?label) = "" || LANGMATCHES(LANG(?label), "en")) 
        } LIMIT 100
    """)
    sparql.setReturnFormat(JSON)
    results = sparql.query().convert()

    arr_items = []
    for result in results["results"]["bindings"]:
        label = result["label"]["value"]
        uri = result["p"]["value"]
        tokens = uri.split("http://dbpedia.org/resource/")
        pizza_name = tokens[1]
        # Format array of items the way pyldapi requires
        arr_items.append( ("{}pizza/{}".format(request.url_root, pizza_name), label, "pizza") )
    return arr_items  

pyldapi expects the array of items in the register to have a particular structure. It requires (URI, URI, label) or (URI, label).

You should now have a working pizza registry at http://localhost:3000/pizza/

4.3. Add the Pizza item views

We now want to enable users to be able to view each Pizza and some details, as well as content negotiate by media type.

4.3.1. Query DBPedia for RDF content

DBPedia has a specific way of providing RDF content via the URL template (text/turtle):

http://dbpedia.org/data/{Name of the Resource}.ttl

We want to load this into our environment and render the information in a tailored way.

4.3.2. Add the Pizza instance model and views

We will create 2 new files - the PizzaRenderer class in the /model/pizza.py file and a Jinja view template file in /view/templates/page_pizza.html

/
-- model/
    -- pizza.py
-- view/
    -- templates/
        page_pizza.html        

4.3.3. Add a PizzaRenderer class in a new pizza model

Add the following code in the pizza-model.txt file in /model/pizza.py.

4.3.4. Update controller/classes.py to query for the pizzas

from model.pizza import PizzaRenderer
from rdflib import Graph
@classes.route('/pizza/<string:pizza_name>')
def pizza_instance(pizza_name):
    instance = None
    pizza_ttl_url = "http://dbpedia.org/data/{}.ttl".format(pizza_name)
    print(pizza_ttl_url)

    #get the Turtle format for the pizza
    r = requests.get(pizza_ttl_url)
    print(r.status_code)
    print(r.text)
    rdf_data = r.text
    #load into RDFLib
    g = Graph()
    g.parse(data=rdf_data, format="turtle")
    instance = {"graph" : g}
    if instance is None:
        return Response("Not Found", status=404)
    renderer = PizzaRenderer(request, request.base_url, instance, 'page_pizza.html')
    return renderer.render()

4.3.5. Add a basic view template for pizza

Create a file called /view/templates/page_pizza.html with the following lines in the pizza-1.txt file in this link.

4.3.6. Modify the Pizza view template and model to render abstract and thumbnail

Add the code in the pizza-model2.txt file in /model/pizza.py.

Modify the __init__ function to add these lines to call the above function in /model/pizza.py:

        self.instance['_data'] = {}
        self._populate_instance_from_rdf()

Add the following code in the pizza-2.txt file in this link

Summary

We have covered how the Pet section of the Example pyLDAPI works using a very basic (JSON) dataset and how to extend it for different LD Views.

We also showed how we can take some existing RDF resources and create a Pizza LD API - Pizza register, Pizza model and views. We showed how to pull in different data content into the views.