In this article, we will extend the simple “Hello World” routine that we created last week to a more formal use. The cloud comes with many features, so we picked one that is called “Tone Analyzer”.

The Tone Analyzer service leverages cognitive linguistic analysis to identify the tone of input content enabling users to refine and improve communications.

We will use this cloud service to transform our basic “Hello World” application into an application that analyzes given text and returns tone scoring per sentence.

Let’s find out how to extend our basic application to use the Tone Analyzer.

Getting the IBM Cloud “Tone Analyzer”

To get the IBM Cloud “Tone Analyzer” you simply order the specific service.

Select the right region and the right plan.

Create the service by clicking Create at the bottom-right side of the page.

You are set to go now!

Visit the service page. It provides all the relevant information for using the Tone Analyzer API. Write down the API key and the full instance reference, you will need them later.

Testing the Tone Analyzer API connection

Test the API connection by executing a web-style call from the command line:

curl -X POST -u "apikey:{apikey}" --header "Content-Type: application/json" --data-binary @tone.json "{url}/v3/tone?version=2017-09-21"

The IBM API documentation provides a few examples on how you can test the API connection using curl.

The tone.json file contains a json data structure:

{

"text": "Team, I know that times are tough! Product sales have been disappointing for the past three quarters. We have a competitive product, but we need to do a better job of selling it!"

}

Extending the basic application

Extending the basic application using the Tone Analyzer means extending the app with a new page accessible via the “/tone” URL on the basic URL. To do so, follow these steps:

1. Add a path to your webapp

To add a new path to the existing app, add an extra mount entry in the builder block and reference the function it should use to handle the request:

our $app = builder {
        mount "/tone" => \&tone;
        mount "/" => \&main;
}

2. Generate form

The “tone” function needs to get input, pass it to the “Tone analyzer” and display the result. At this point we will not focus on the graphics but functionality.

It is good practice for any web application to be consistent. That is why most functions that handle input and output are self-referencing. What this means is that the “tone” function should generate the form as well as process the content of the form. Making the function generate the form can be easily implemented:

my $res = Plack::Response->new(200);
	$res->body("<html><body><pre>$data</pre><form><textarea cols=99 rows=20 name=\"text\">$text</textarea></br><input type=\"submit\"; value=\"Analyze tone\"></form></body></html>");
	return $res->finalize;

Note that there are a couple of blanks here. The variable “$data” should hold the result of our analysis, the variable “$text” needs to contain the “current” content as it is submitted. At first call, when both variables are empty, this will produce an empty form with no additional information.

3. Process the content of the form

Next you have to access the data that is past to the app accessing the page. For this, you need to know any call from a mounted point which will be executed carrying the environment forward.

This environment contains a read/write accessible copy of all information passed from and to the environment.

To access parameters passed via the web call, use the Request library converting the data into an object. Store this object in the environment itself for current and future reference. From this object you can easily access the data using the “param” function:

my $env = shift;
        $env->{req} = Plack::Request->new($env);

        my $text = $env->{req}->param("text");

Set up a local “UserAgent” that will interact with the Tone Analyzer” API.

our $ua = LWP::UserAgent->new(timeout => 10);

To get the sentiment of sentences, execute a web call to the API and convert the data that is received into some form of output.

We convert the JSON structured data into a Perl like structure and dump it into a plain text formatted area on the screen:

my $result = $ua->post($api_url, "Content-Type" => "application/json", Content => encode_json({text => $text}));
                if($result->is_success) {
                        $data = Dumper decode_json $result->decoded_content;
                } else {
                        $data = $result->status_line;
                }

The API URL has the following format:

our $api_url = "https://apikey:$APIKEY\@$APIURI/v3/tone?version=2017-09-21";

where both the API KEY and API URI can be found in the IBM web interface (service page).

If the web call fails, the page will only visualize the status line of the API call. For the list of possible error messages, consult the API documentation.

The content of the app.psgi script

Once you’ve completed the steps above, the content of the app.psgi script should look something like this:

#! /usr/bin/perl

use Plack::Builder;
use Plack::Request;
use Plack::Response;
use LWP::UserAgent;
use JSON;
use Data::Dumper;
use strict;

our $api_key = "XXXXXXXX";
our $api_ins = "api.eu-de.tone-analyzer.watson.cloud.ibm.com/instances/XXXX";
our $api_url = "https://apikey:$api_key\@$api_ins/v3/tone?version=2017-09-21";
our $ua = LWP::UserAgent->new(timeout => 10);

sub tone {
        my $env = shift;
        $env->{req} = Plack::Request->new($env);

        my $text = $env->{req}->param("text");
        my $data = "";
        if($text) {
                my $result = $ua->post($api_url, "Content-Type" => "application/json", Content => encode_json({text => $text}));
                if($result->is_success) {
                        $data = Dumper decode_json $result->decoded_content;
                } else {
                        $data = $result->status_line;
                }
        }

        my $res = Plack::Response->new(200);
	$res->body("<html><body><pre>$data</pre><form><textarea cols=99 rows=20 name=\"text\">$text</textarea></br><input type=\"submit\"; value=\"Analyze tone\"></form></body></html>");
	return $res->finalize;
}

sub main {
        my $env = shift;

        my $res = Plack::Response->new(200);
        $res->content_type("text/plain");
        $res->body("Hello World");

        return $res->finalize;
}

our $app = builder {
        mount "/tone" => \&tone;
        mount "/" => \&main;
}

4. Upgrade SourceyBuild.sh.

Upgrade SourceyBuild.sh by adding all the libraries necessary for running your app in the cloud:

# if you want to see what happens in more detail
SOURCEY_VERBOSE=1

# if you want to force sourcey to rebuild everything
SOURCEY_REBUILD=0

# create a copy of perl
#buildPerl 5.22.1

buildPerlModule PSGI
buildPerlModule Plack
buildPerlModule Plack::Runner
buildPerlModule Plack::Request
buildPerlModule Plack::Response
buildPerlModule Plack::Builder
buildPerlModule Data::Dumper
buildPerlModule JSON
buildPerlModule LWP::UserAgent
buildPerlModule LWP::Protocol::https

Please note the last line above. The UserAgent library does not support secure “https” calls by default. Since our API’s default behavior is to support https, we include the LWP https library to make sure that our call gets executed correctly.

The Sourcey framework offers extensive functionality but as this is out of scope for our current example we will not dive into more details. Suffices to say that we just added the needed libraries.

5. Push the new app to the cloud

To push the extended application to the cloud, execute ibmcloud cf push.

You can now browse to the new URL:

Enter some text and click Analyze tone. You get a tone analysis of the sentences in the text:

The HTML doesn’t particularly have a great quality, but in this post we focused on functionality not on the layout.

In a future post we’ll transform this rude output into something more elegant and eye catching.

 

Drop me a note below with your thoughts and questions about building cloud applications and I’ll do my best to answer them in future posts.