Web App Settings

This section outlines the responsive web app configuration options. Please visit the getting started section if you haven’t set up a model yet.

All of the sections below are available within the Settings panel of an ML model:

web app tabs

Features Schema

The features schema is used to configure how inputs are gathered for your ML model in the web app.

Without a features schema, the demo form contains a single free-form input field. In this field, you are able to enter a comma-separated list of inputs (make sure the data types match the model for each input in the list) and see the results:

without features schema

Instead of forcing your users to use a comma-separated list, you probably want a smoother experience. Luckily, we can use the features schema to add select boxes for categorical attributes, ensure proper data types, provide default values, and more.

Define Features Schema Structure

Booklet utilizes a JSON-formatted structure to define the features schema. First you should define the name and default value for each of the features. The name is a string. The default value provides the initial value for each feature and is used to infer the data type. You can input a string, a float, or an integer. Here is an example schema with two features, both of which are floats:

[
  {
    "name": "Petal Length (cm)",
    "default": 5.4
  },
  {
    "name": "Petal Width (cm)",
    "default": 2.1
  }
]

Booklet will use this features schema to create the form below:

simple features schema

Free-form text features

As Booklet infers the input type from the default value, provide a default value of type string to create a free-form text entry field. For example, this features schema:

[
  {
    "name": "Text",
    "default": "The Wimbledon tennis tournament starts next week!"
  }
]

Creates the following form:

text no options

Select Box

To provide a pre-filtered list of options (useful for categorized features), use the options attribute:

{
    "name": "lead source",
    "default": "Direct Traffic",
    "options": [
      "Direct Traffic",
      "Google",
      "Olark Chat",
      "Organic Search",
      "Referral Sites"
    ]
}

The options above create the following form:

text with options

Multi-select

To allow a user to select multiple values, set "multiple": true. The outputs will be concatenated together with spaces. This is ideal for a “bag-of-words” NLP model:

[
  {
    "name": "Programming Language (one or many)",
    "default": "Python",
    "options": [
      "JavaScript",
      "Python",
      "HTML",
      "Jupyter",
      "TypeScript",
      "R",
      "CSS"
    ],
    "multiple": true
  }
 ]

Here is how the multi-select appears in the form:

text multi select

Features Schema Validation

Booklet automatically adds data validation to your form based data type inferred from the default value (string, float, or integer). If the inputted value is of the incorrect type or missing, an error will appear and a prediction will not be allowed:

features validation

Post-Processing

Booklet returns the first element from your model’s output result when the demo form is submitted. For example, a model that returns [2] or [[2],[3]] will display 2 in the UI result:

default post processing

See our output schema page for more information on supported output formats.

Using the Post-Processing Editor

You likely want to spruce-up the formatting a bit for these processed results. This may include formatting, interpretation, or even adding images. To support any number of options for how the results are formatted, Booklet utilizes the Nunjucks formatting language. It’s a simple language, built on JavaScript and similar to Python’s Jinja, and it offers unlimited flexibility on how the results will be processed.

To make iterating on the template design easier, Booklet offers a simple editing panel which displays the results in real-time. All previews are based on the default values inputted in the feature schema. Booklet automatically runs a single prediction against your model and populates the example model result based on on that prediction.

If you open up the post-processing panel editor for the first time, you’ll see the default Nunjucks command in the editor, and the results from the default prediction in the preview. This is the default Nunjucks formatting that simply displays the results unaltered. Here is the post-processing editor with different sections labeled:

feature schema layout

Using Nunjucks for Post-Processing

To learn more about Nunjucks, check out the official documentation.

For Booklet specifically, you’ll be working with the result object, which you can preview within the post-processing editor. Also, if your result is more than just a single result and instead a dictionary of some kind, you can access an individual result within with result.<key> and so-on. Let’s go through a few examples:

Example 1: If Statement

This example converts a binary output into text for easier understanding by the user.

In this case the result object is a simple 1 or 0:

result = 1

If the result is a 0 we want to show Not Likely to Convert and if the result is a 1 we want to show Likely to Convert. To configure this, we can write up a simple if statement in Nunjucks:

{% if result < 1 %}
  Not Likely to Convert
{% else %}
  Likely to Convert
{% endif %}

Now, in the post-processing editor, we can see the that the default result corresponds to Likely to Convert:

nunjucks example 1

Example 2: If Statement plus Additions

For this next example, our result is a float that ranges from 0.0 to 3.0. We want to convert those values into text explanations, display the original result, and finally display an image. First, lets check out the example result:

result = 2.0

To setup the output in this way, we need to setup an if-statement again. But this time, we use the if-statement to set the variables: name for the text explanation and image_url for the image link. We then display those alongside the actual result:

{% if result <= 0.49 %}
  {% set name = "Iris Setosa" %}
  {% set image_url = "/images/responses/iris/setosa.jpg" %}
{% elif result <= 1.49 %}
  {% set name ="Iris Versicolor" %}
   {% set image_url = "/images/responses/iris/versicolor.jpg" %}
{% else %}
  {% set name = "Iris Virginica"  %}
  {% set image_url = "/images/responses/iris/virginica.jpg" %}
{% endif %}

{{ name }} (raw result = {{ result }})
<img src="{{ image_url }}"/>

Here you can see the text explanations, the original result, and an image for the result:

nunjucks example 2

Example 3: Parsing a more complex result

In some cases, your result object might be more complicated than a single number. In this case, the object contains both probabilities and n-grams, each of what are dictionaries inside of the large dictionary:

result = {
  "probabilities": {
    "Sports": 0.6436089277267456,
    "Business": 0.13958673179149628,
    "World": 0.1301129013299942,
    "Sci/Tech": 0.08669137209653854
  },
  "top_n_grams": {
    "2": "starts next",
    "3": "starts next week",
    "4": "week"
  }
}

We want to make this result a little easier to read for our web app user. To do that, we create a table for each set of results (probabilities and n-grams), and then clean up the formatting a bit. To do that, we will use a combination of HTML (for the table structures) and Nunjucks (to fill in the values). You can see that we access the set of probabilties with {% for category, prob in result.probabilities %}:

<h5>Probabilities</h5>
<table style="width:100%;margin-bottom:20px">
<tr><th>Category</th><th>Prob %</th></tr>
{% for category, prob in result.probabilities %}
<tr>
<td>{{category}}</td><td>{{ (prob*100) | round(1) }}</td>
</tr>
{% endfor %}
</table>
<h5>Top N Grams</h5>
<table style="width:100%">
<tr><th>Index</th><th>Sequence</th></tr>
{% for count, value in result.top_n_grams %}
<tr>
<td>{{count}}</td><td>{{ value }}</td>
</tr>
{% endfor %}
</table>

This now formats the results into an easy-to-understand structure:

nunjucks example 3

Example 4: Complex Result and Web Formatting

Our final example highlights how a complex formatting structure is also possible with Booklet. In this case, we have a list of 10 results, each of which contains information about a GitHub Repo:

result = [
  {
    "github_repo_url": "https://github.com/mmarchegiani/covid-19",
    "repo_description": "data analysis of the covid-19 outbreak",
    "topics": "",
    "owner_repo_name": "mmarchegiani/covid-19",
    "owner_name": "mmarchegiani",
    ...
  },
  {
    "github_repo_url": "https://github.com/covid19db/fetchers-python",
    "repo_description": "data source fetchers written in python",
    "topics": "",
    "owner_repo_name": "covid19db/fetchers-python",
    "owner_name": "covid19db",
    ...
  },
  ...
]

We want to make this list appear as a list of GitHub repositories, as if we were actually on GitHub. To set that up, we can use the following Nunjucks code:

  <h5 class="mt-3"><svg height="24" class="octicon octicon-mark-github" viewBox="0 0 16 16" version="1.1" width="24" aria-hidden="true" style="margin-right: 5px; vertical-align: top"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path></svg>
  Most Relevant GitHub Repositories</h5>
  <hr/>
  <ul style="list-style-type:none;padding-left:0">
  {% for repo in result %}
  <li style="">
  <svg height="16" style="fill: gray;margin-right: 5px" class="octicon octicon-repo" viewBox="0 0 12 16" version="1.1" width="12" aria-hidden="true"><path fill-rule="evenodd" d="M4 9H3V8h1v1zm0-3H3v1h1V6zm0-2H3v1h1V4zm0-2H3v1h1V2zm8-1v12c0 .55-.45 1-1 1H6v2l-1.5-1.5L3 16v-2H1c-.55 0-1-.45-1-1V1c0-.55.45-1 1-1h10c.55 0 1 .45 1 1zm-1 10H1v2h2v-1h3v1h5v-2zm0-10H2v9h9V1z"></path></svg>
  <a href="{{ repo.github_repo_url }}" target="_blank">{{ repo.owner_repo_name }}</a>
  <p class="mb-0">{{ repo.repo_description }}</p>
  <p class="small text-muted">
  <svg aria-label="star" class="octicon octicon-star" style="fill:gray; vertical-align: top;padding-top:3px" viewBox="0 0 14 16" version="1.1" width="14" height="16" role="img"><path fill-rule="evenodd" d="M14 6l-4.9-.64L7 1 4.9 5.36 0 6l3.6 3.26L2.67 14 7 11.67 11.33 14l-.93-4.74L14 6z"></path></svg>
  {{ repo.count_of_stars }}
  &nbsp;&nbsp;
  {{ repo.primary_language_name }}
  &nbsp;&nbsp;
  {% if repo.license_name %}
  {{ repo.license_name }} license
  &nbsp;&nbsp;
  {% endif %}
  Created {{ repo.repo_created_day }}
  </p>
  </li>
  {% endfor %}
  </ul>

You can see that the results look just like a list of GitHub Repositories:

nunjucks example 4

If you have questions about setting up the web app, please feel free to reach out to us at support@booklet.ai