In this codelab we will add a "view project" feature to our frontend web app that can replace our use of the "print_project.py". The final product will look like this:

We will first begin with the frontend code from previous labs. ssh into your frontend VM instance and within your repository, create a directory named sawtooth04 and copy the contents of sawtooth02 over to it.

mkdir <your-course-repo>/sawtooth04
cd <your-course-repo>
cp -r sawtooth02/* sawtooth04

Then, add, commit and push the new files.

git add sawtooth04
git commit -m "sawtooth04 initial commit"
git push

We will be extending the code in sawtooth04 with the functionality in this codelab.

First, we must query state from the REST API of the backend VM instance we've just implemented. The backend gives us results in JSON format, which we can decode to a Python dictionary. In the next step, we will implement the function for extracting the data we need from the JSON dictionary. This function simply sets the global variables "project_node" and "tasks" and then calls the render function to render the webpage with the new project displayed.

webapp.py

@app.route('/viewproject',methods=['POST'])
def view_project():
    project_name = request.form['project_name']
    with urllib.request.urlopen("http://<IPaddr_of_backend>:8008/state") as url:
        state = json.loads(url.read().decode())['data']
    global project_node
    global tasks
    tasks = []
    project_node = getProjectNode(state, project_name)
    for task_name in project_node.task_names:
        tasks.append(getTask(state, project_name, task_name))
    return redirect(url_for('render'))

The getData function simply pulls data from the JSON object using the address of the data from the merkle tree. It then decodes the data from base64 and returns it.

webapp.py

def getData(state, address):
    ''' Gets the data from a provided address.

        State has two fields address and data.  We can create the
        address using functions in addressing.py.  The data field
        is encoded with base64 encoding.
    '''

    for location in state:
        if location['address'] == address:
            encoded_data = location['data']
            return base64.b64decode(encoded_data)
    return None

The following two functions get data from and address using the previous function. From there, they decode it using protobuf, find the correct entry in the container, and return it.

webapp.py

def getProjectNode(state,project_name):
    ''' Given a project name get a project node. '''

    # make address of project metanode 
    project_node_address = addressing.make_project_node_address(project_name)
    project_node_container = ProjectNodeContainer()
    data = getData(state,project_node_address)
    project_node_container.ParseFromString(data)  # decode data and store in container
 
    for project_node in project_node_container.entries:  # find project with correct name
        if project_node.project_name == project_name:
            return project_node
    return None

def getTask(state, project_name,task_name):
    ''' Given a project name and task name get a task node. '''

    # make address of task node
    task_address = addressing.make_task_address(project_name, task_name)
    task_container = TaskContainer()
    data = getData(state,task_address)
    task_container.ParseFromString(data)  # decode data and store in container

    for task in task_container.entries:  # find task with correct name
        if task.task_name == task_name:
            return task
    return None

Now that we have pulled and decoded the data from state, we can display it in the HTML. Recall that the web application calls the render() function when the web site is visited as shown below:
webapp.py

@app.route('/')
def render():
    return render_template('page.html', fields=fields, action=action, project_node=project_node, tasks=tasks)

This function performs a render_template operation on page.html. We can add additional HTML to page.html in order to display project information. Add the following HTML code to the bottom of the template. The code includes a form that has one text input and a submit button that will call view_project and pass it the project name from the text input. Underneath this the code for displaying a project is shown. It simply displays the project name, iterates through and displays the public keys, and then iterates through and displays the tasks.

templates/page.html

   <h2>View Project</h2>
   <form action="{{ url_for('view_project') }}" method=post>
       <p class="heading">Project Name: <input value="{{project_node.project_name}}" type=text name=project_name><input type=submit value=View></p>
   </form>
   <h3>Project Name: {{ project_node.project_name }}</h3>
   <h3>Public Keys:</h3>
   {% for public_key in project_node.public_keys %}
   <p>{{public_key}}</p>
   {% endfor %}
   <h3>Tasks:</h3>
   {% for task in tasks %}
   <h4>Task: {{task.task_name}}</h4>
   <p>Description: {{task.description}}</p>
   <p>Progress: {{task.progress}}</p>
   {% endfor %}
{% endblock %}

Make sure your code works: Try creating a project and make sure it displays correctly: