Skip to content
This repository was archived by the owner on Apr 6, 2020. It is now read-only.

5.1. Tutorial 1 Random generator

s.mombuleau edited this page Dec 4, 2018 · 2 revisions

Random generator

In the first tutorial of this series, we are going to develop a Random Generator. We want our random generator to:

  • Give us the result of a toss coin (heads/tails) -> "random toss"
  • Give us the result of a Rock, Paper, Scissors game -> "random shifumi"
  • Give us a random number between X and Y -> "random between X and Y"
  • Give us a random value from a set of data -> "random from (W, X, Y, Z, ...)"

Example:

random toss
-> head  s

random shifumi
-> paper

random between 1 and 100
-> 42

random from (Pizza, Burgers, Kebab, Chinese food)
-> Burgers

Generate our IA

We're going to start off by generating our new IA:

$ new
  
  IA-Core v3.1.1 - IA Generator
  
  -----------HELP SECTION-----------
  
  Name:                   Name of your Instant Answer
  Description:            Description of your Instant Answer
  Keyword:                WHAT keyword should trigger the IA ? ex: "weather Paris", weather is the keyword
  Trigger:                WHERE should the keyword be expected in a query ? ex: "weather Paris" = start
  Modifiers:              Regex modifier(s), see https://www.w3schools.com/jsref/jsref_obj_regexp.asp
  Javascript script:      Front script, used to interact with how your IA displays, can be modified later
  Timeout:                Time before your response is considered as canceled (in milliseconds)
  Cache:                  Duration of the cached data (in seconds)
  Order:                  Order in the IA hierarchy (0 = first, 1 = second, ... no order = added at the end, alphabetically)
  
  ? Name Random generator
  ? Description Generates a random from: coin toss, rock paper scissors, a set of values or between two integers
  ? Keyword random
  ? Trigger start
  ? Modifiers i (case insensitive matching)
  ? Javascript script No
  ? Timeout (in milliseconds) 3600
  ? Cache duration (in seconds) 10800
  ? Order in the hierarchy (integer or hit enter for no order)
  
  Generating "Random generator" IA...
  
  src/modules/random_generator created
  src/modules/random_generator/random_generator.js created
  src/modules/random_generator/public created
  src/modules/random_generator/public/random_generator.dot created
  src/modules/random_generator/public/css created
  src/modules/random_generator/public/css/random_generator.css created
  src/modules/random_generator/lang_src created.
  src/modules/random_generator/lang_src/fr.po created.
  src/modules/random_generator/lang_src/de.po created.
  src/modules/random_generator/lang_src/en.po created.
  src/modules/random_generator/lang_src/co.po created.
  src/modules/random_generator/lang_src/br.po created.
  src/modules/random_generator/lang_src/eu.po created.
  src/modules/random_generator/lang_src/ca.po created.
  src/modules/random_generator/lang_src/es.po created.
  src/modules/random_generator/lang_src/it.po created.
  src/modules/random_generator/lang_src/nl.po created.
  src/modules/random_generator/lang_src/ru.po created.
  src/modules/random_generator/lang_src/pl.po created.
  src/modules/random_generator/lang_src/pt.po created.
  
  IA "Random generator" generated.

As you can see, we have already decided of a few things:

  • Our IA will be named Random generator and its folder will be random_generator
  • The keyword that will call our IA is random
  • The trigger has been set to start because we expect the keyword to be at the start of our query ("random [toss/shifumi/between X and Y/from (Set, Of, Values)]")
  • We don't need a front javascript script because we don't need to interact with how things are displayed or work for that IA

Testing your IA

Every time you make a change to your IA, the watcher will react and reload the app for you.

  instant-answers_1  |     info: Loading modules...
  instant-answers_1  |     info: Front started, listening on port 3030 -> http://localhost:3030/
  instant-answers_1  |     info: calculator loaded
  instant-answers_1  |     info: currency_converter loaded
  instant-answers_1  |     info: current_time loaded
  instant-answers_1  |     info: current_week loaded
  instant-answers_1  |     info: encryption loaded
  instant-answers_1  |     info: hello_world loaded
  instant-answers_1  |     info: ia_test loaded
  instant-answers_1  |     info: npm loaded
  instant-answers_1  |     info: timer loaded
  instant-answers_1  |     info: unit_converter loaded
  instant-answers_1  |     info: weather not loaded (excluded) module=ia module loader
  instant-answers_1  |     info: IA Order: 0: calculator, 20: currency_converter, 30: encryption, 60: npm, 80: unit_converter, 82: current_time, 103: current_week, no order: hello_world, no order: ia_test, no order: timer
  instant-answers_1  |     info: Server started, listening on port 3002 -> http://localhost:3002/
  instant-answers_1  |     [nodemon] restarting due to changes...
  instant-answers_1  |     [nodemon] restarting due to changes...
  instant-answers_1  |     [nodemon] starting `node front/bin/www`
  instant-answers_1  |     [nodemon] starting `node bin/www`
  instant-answers_1  |     info: Starting IA-Core v3.1.1 server...
  instant-answers_1  |     
  instant-answers_1  |     info: Loading modules...
  instant-answers_1  |     info: Front started, listening on port 3030 -> http://localhost:3030/
  instant-answers_1  |     info: calculator loaded
  instant-answers_1  |     info: currency_converter loaded
  instant-answers_1  |     info: current_time loaded
  instant-answers_1  |     info: current_week loaded
  instant-answers_1  |     info: encryption loaded
  instant-answers_1  |     info: hello_world loaded
  instant-answers_1  |     info: ia_test loaded
  instant-answers_1  |     info: npm loaded
  instant-answers_1  |     info: random_generator loaded
  instant-answers_1  |     info: timer loaded
  instant-answers_1  |     info: unit_converter loaded
  instant-answers_1  |     info: weather not loaded (excluded) module=ia module loader
  instant-answers_1  |     info: IA Order: 0: calculator, 20: currency_converter, 30: encryption, 60: npm, 80: unit_converter, 82: current_time, 103: current_week, no order: hello_world, no order: ia_test, no order: random_generator, no order: timer
  instant-answers_1  |     info: Server started, listening on port 3002 -> http://localhost:3002/

As you can see, the server restarted on its own and the random_generator module loaded properly.

You can then hit the server directly from your browser. Queries should be passed through the ?q= argument, and an optional ?lang= argument can be passed as well:

http://localhost:3002/?q=random shifumi

This will return a json object containing your answer or an error. Right now this query should timeout, as our app is not processing it yet.

http://localhost:3002/?q=random shifumi
{
    "error": 500,
    "status": "error",
    "message": "operation timed out"
}
/solve endpoint with "ia" parameter

To make sure to trigger your IA and not another one, you can force the API to point to an IA through the /solve endpoint. That way, if it still doesn't trigger, you will know there's an issue with your triggers. If it triggers when you force it, maybe there's another IA getting triggered before it. You might want to check your regex.

http://localhost:3002/solve?q=test&ia=random_generator
{
    "error": 500,
    "status": "error",
    "message": "module random_generator doesn't exist or bad query"
}

Because we have set the trigger to random, the query is not recognized.

http://localhost:3002/solve?q=random shifumi&ia=random_generator
{
    "error": 500,
    "status": "error",
    "message": "operation timed out"
}

It triggers the IA correctly, but still times out because we haven't done anything yet.

Let's code!

Our IA has been generated and every file we will need has been created. Now that we have our data, and our images, we need to open our project in our preferred IDE so we can start coding.

App file

The first file we are going to edit is the main file of our module. It should contain everything to already run properly, but the result is not yet calculated. We need to process the query and send the right result.

The getData() function gets the query as an array of values. This is where we'll determine which random we'll do, and do it:

app/src/modules/random_generator/random_generator.js
getData: function (values, proxyURL, language, i18n) {
    const _ = i18n._;
    return new Promise(function (resolve, reject) {
        // do something with the data (values)
    });
},

We're going to start by checking that the query is complete and that we do have a proper string to process:

app/src/modules/random_generator/random_generator.js
getData: function (values, proxyURL, language, i18n) {
    const _ = i18n;
    return new Promise(function (resolve, reject) {
        if (values[2]) {
            // for "random toss", values[2] == "toss"
            // for "random shifumi", values[2] == "shifumi"
            // for "random between 1 and 100", values[2] == "between 1 and 100"
            // for "random from (1, 2, 3)", values[2] == "from (1, 2, 3)"
        } else {
            reject("Couldn't process query.")
        }
    });
},

Then, we are going to check which random we want to generate. First, we are going to create a function that's going to take our query (values[2] here) and determine from that which algorithm is going to be called:

app/src/modules/random_generator/random_generator.js
/**
 * determines which random algorithm is going to be used depending on the query we're getting
 * @param query
 * @returns {*}
 */
function getRandomAlgo(query) {
    var regex_from = /from \(((?:[^,]*\s*,?\s*)*)\)/;
    var regex_between = /between (\d+) and (\d+)/;
    var from_values;
    var between_values;
    if (query === "toss" || query === "shifumi") return [query, ''];
    if ((from_values = query.match(regex_from))) return ["from", from_values];
    if ((between_values = query.match(regex_between))) return ["between", between_values];

    return null;
}

Then, we're going to use this function in our getData function and process the result to show:

app/src/modules/random_generator/random_generator.js
getData: function (values, proxyURL, language, i18n) {
    const _ = i18n._;
    return new Promise(function (resolve, reject) {
        if (values[2]) {
            var random_algo = getRandomAlgo(values[2]); // depending on the query, we determine which algorithm to use
            if (random_algo) {
                switch (random_algo[0]) {
                    case "toss": // example: "random toss"
                        // we just need a random of 2. 0 = "heads", 1 = "tails"
                        resolve((Math.floor(Math.random() * 2) === 0) ? "heads" : "tails");
                        break;

                    case "shifumi": // example: "random shifumi"
                        // we need a random of 3. 0 = "rock", 1 = "paper", 2 = "scissors"
                        var shifumi = ["rock", "paper", "scissors"];
                        var chance = Math.floor(Math.random() * 3);
                        resolve(shifumi[chance]);
                        break;

                    case "from": // example: "random from (X, Y, Z)" where X, Y and Z can be anything
                        // we need to split the string returned by the regex which contains "X, Y, Z"
                        if (random_algo[1] && random_algo[1][1]) {
                            var from = random_algo[1][1].split(",");
                            // we then do a random of the number of items in "from"
                            var rand = (Math.floor(Math.random() * from.length));
                            resolve(from[rand].trim()); // 0 = X, 1 = Y, 2 = Z... trimmed to remove whitespaces
                        } else {
                            reject("Couldn't process query.");
                        }
                        break;

                    case "between": // example: "random between X and Y" where X and Y are integers
                        if (random_algo[1].length > 1) {
                            // we're getting X and Y thanks to the regex in getRandomAlgo()
                            var random_min = parseInt(random_algo[1][1]);
                            var random_max = parseInt(random_algo[1][2]);

                            if (random_min > random_max) {
                                var tmp = random_min;
                                random_min = random_max;
                                random_max = tmp;
                            }

                            // we then do a random to get a number in this range
                            resolve((Math.floor(Math.random() * (random_max - random_min + 1)) + random_min));
                        } else {
                            reject("Couldn't process query.");
                        }
                        break;
                }
            } else {
                reject("Couldn't process query.");
            }
        } else {
            reject("Couldn't process query.");
        }
    });
},
Template

Now that we have our data, we have to display it. We need to edit the template file and write some HTML:

app/src/modules/random_generator/public/random_generator.dot
<!--
 This is your main template file. Please refer to the documentation for more information.
-->
<link rel="stylesheet" href="css/random_generator.css" type="text/css">
<div class="ia__random_generator">
    <span class="ia__random_generator__result">
        {{= it.data }}
    </span>
</div>

As you can see, we have created some HTML elements that will dictate, alongside the .css file, how the IA looks like.

The object it contains the answer from the server. You can already guess that it.data corresponds to what the getData() function returns.

Stylesheet

Styling your IA is important, because it has to look good for every user, no matter what their browser or device might be.

We have written Styling Guidelines that should help you achieve that.

For this IA, the styling is pretty straight forward and shouldn't require much explanation:

app/src/modules/random_generator/public/css/random_generator.scss
.ia__random_generator {
    padding: 40px;
}

.ia__random_generator__result {
    font-size: 14px;
}
Localization (app/src/modules/random_generator/public/lang_src/*.po)

We won't cover localization for that tutorial. If you want to translate your Random Generator to other languages and have it work in your code, please see 5.2. Tutorial 2: Country Flags

Testing the data

Now that those changes were made, we can try querying the server again:

http://localhost:3002/?q=random shifumi
{
    "runtime": "nodejs",
    "template_name": "random_generator",
    "display_name": "Random generator",
    "images_path": "ia/img/",
    "data": "scissors",
    "query": "random shifumi",
    "status": "success",
    "cacheExpirationTime": 10800,
    "files": [
        {
            "url": "ia/template/en_gb/random_generator.js?5bdcd3c9a3787de3d0bd71776359e81e8f79274b",
            "type": "template"
        }
    ]
}
http://localhost:3002/?q=random toss
{
    "runtime": "nodejs",
    "template_name": "random_generator",
    "display_name": "Random generator",
    "images_path": "ia/img/",
    "data": "tails",
    "query": "random toss",
    "status": "success",
    "cacheExpirationTime": 10800,
    "files": [
        {
            "url": "ia/template/en_gb/random_generator.js?5bdcd3c9a3787de3d0bd71776359e81e8f79274b",
            "type": "template"
        }
    ]
}
http://localhost:3002/?q=random between 1 and 42
{
    "runtime": "nodejs",
    "template_name": "random_generator",
    "display_name": "Random generator",
    "images_path": "ia/img/",
    "data": 22,
    "query": "random between 1 and 42",
    "status": "success",
    "cacheExpirationTime": 10800,
    "files": [
        {
            "url": "ia/template/en_gb/random_generator.js?5bdcd3c9a3787de3d0bd71776359e81e8f79274b",
            "type": "template"
        }
    ]
}
http://localhost:3002/?q=random from (Burgers, Pizza, Pasta)
{
    "runtime": "nodejs",
    "template_name": "random_generator",
    "display_name": "Random generator",
    "images_path": "ia/img/",
    "data": "Pizza",
    "query": "random from (Burgers, Pizza, Pasta)",
    "status": "success",
    "cacheExpirationTime": 10800,
    "files": [
        {
            "url": "ia/template/en_gb/random_generator.js?5bdcd3c9a3787de3d0bd71776359e81e8f79274b",
            "type": "template"
        }
    ]
}

Make sure to launch the query several times to see that your data is actually random! Our IA works!

Sandbox testing

Now that we made sure that our data is correct, we're going to test how our IA looks like.

Simply navigate to http://localhost:3030/?q=random shifumi:

Make sure that:

  • Your IA is triggered correctly,
  • Your IA blends in correctly with Qwant's layout
  • Your IA actually gives the right result and works the way you intended (type "random shifumi"!)
Clone this wiki locally