How to Collect Activity Info with xAPI and Storyline

In this tutorial, we’ll take a closer look at the activity information we can collect with xAPI. We will also make our statements more robust by providing additional context and “type” information — this makes our statements easier to read and query.

We will pick up from where we left off with the Getting Started with xAPI Tutorial Series, so to follow along and get the most out of this tutorial, you should have completed the series.

Before diving in, open up your xapi-statement.js file that we finished during Part 3 of the Getting Started series. We will add to this code later in the tutorial.

What is an xAPI Activity?

First thing’s first — what’s an activity? In our previous tutorials, we discussed xAPI Objects in the Actor-Verb-Object model. Put simply, an activity is the most common type of object. So, another way we can think about our model is Actor-Verb-Activity.

For example, Devlin (actor) completed (verb) Collecting Activity Info xAPI Tutorial (object / activity).

But, if an activity is the most common type of object we deal with, what are the other object types? They are as follows:

  • Agent. For example, Devlin (actor) greeted (verb) Susie (agent).
  • Group. For example, Devlin (actor) greeted (verb) Management (group).
  • StatementRef. With this object type, the statement refers to a previously existing statement’s ID. For example, Devlin (actor) reported (verb) statement with ID: [insert ID here] (statement reference).
  • Sub-statement. Sub-statement object types are used to imply future intent. For example, Devlin (actor) planned (verb) Devlin wrote xAPI tutorial (sub-statement).

These other object types are not important for the purposes of this tutorial, and you likely will not need to send any of them from a Storyline course. However, if you’d like to read more to further develop your understanding of xAPI, you can do so here.

The important thing is that you know we’re working with activity object types.

Changing the xAPI Object’s Type

Now that we know we’re using an activity as our object, we can reflect this in the xAPI statement.

Let’s take a look at the Object JSON object that we have in our xAPI statement already:

"object": {
  "id": objectId,
  "definition": {
    "name": { "en-US": object }
  }
}	

(If this code is unfamiliar to you, please complete the Getting Started with xAPI Tutorial Series before continuing)

The “objectType” key:value pair is a property of the object itself, so let’s try adding that after the definition now:

"object": {
  "id": objectId,
  "definition": {
    "name": { "en-US": object }
  },
  "objectType": "Activity"
}	

Important note: Since we’re adding another key to our object, don’t forget to add a comma after the definition JSON object.

Nice work! Now, when this statement sends, it will tell the LRS that the Object is an Activity (as opposed to an agent, group, statement, or sub-statement). This will come in hand when it comes time to query statements from the LRS.

Further Defining the Object

As our object currently stands, we don’t have a ton of information about the activity that’s occurring. We know that it’s an activity, and we can pass it an ID and “name” using the sendstatement function’s parameters (See <a href=”https://www.devlinpeck.com/posts/send-xapi-statement-storyline/#set-parameters” target=”blank”>this section in Part 3 of the Getting Started series if you’re not familiar with parameters).

However, what if we want to provide additional information about what the activity is?

Adding the Object Definition's Description

Consider our example statement:

Devlin completed Collecting Activity Info xAPI Tutorial.

We can provide additional information about “Collecting Activity Info xAPI Tutorial” by adding to the object’s “definition” property. Particularly, we can add a “description”, like so:

"object": {
  "id": objectId,
  "definition": {
    "name": { "en-US": object },
    "description": { "en-US": "Beginner xAPI Tutorial on devlinpeck.com" }
  },
  "objectType": "Activity"
}	

Notice that the description key’s value is also an object, and it takes a lanaguage map value just like “name.”

This description provides additional information about the object so that whoever is analyzing it can make more sense of it.

Adding the Object Definition's Type

We’ve already modified our statement’s object to say that it’s an activity, but what if we want to say which type of activity it is? The xAPI specification allows for this by letting us add a “type” property to the object’s definition, like so:

"object": {
  "id": objectId,
  "definition": {
    "name": { "en-US": object },
    "description": { "en-US": "Beginner xAPI Tutorial on devlinpeck.com" },
    "type": "http://activitystrea.ms/schema/1.0/article"
  },
  "objectType": "Activity"
}	

Since the Collecting Activity Info xAPI Tutorial is an article on a website, we can assign it the “article” type.

As you may have noticed, the “type” key matches up with a URI (unique identifier, often in the form of a URL), and you can find a community-generated and mutually agreed-upon set of actvitiy types on the Registry.

I recommend using types from the registry instead of trying to make and define your own.

Also, if you use the “interaction” type, then some additional information is required. We will not delve into that here, but you can check out the Deep Dive: Activity article on xapi.com to learn more.

Adding Parameters to the Function

Now we’ll take a look at our send_statement function as a whole. Since the object defintion’s description and activity type will vary depending on the statement that you’re trying to send, we will want to add parameters (and then replace the strings with those variables) to ensure that you can easily change their values when sending a statement from Storyline.

Let’s add an objectDescription and activityType parameter to the function and replace the strings accordingly. Your complete send_statement function should now look as follows:

function send_statement(verb, verbId, object, objectId, objectDescription, activityType) {
  const player = GetPlayer();
  const uNamejs = player.GetVar("uName");
  const uEmailjs = player.GetVar("uEmail");
  const conf = {
    "endpoint": "https://trial-lrs.yetanalytics.io/xapi/",
    "auth": "Basic " + toBase64("1212:3434")
  };
  ADL.XAPIWrapper.changeConfig(conf); 
  const statement = {
    "actor": {
      "name": uNamejs,
      "mbox": "mailto:" + uEmailjs 
    },
    "verb": {
      "id": verbId,
      "display": { "en-US": verb }
    },
    "object": {
      "id": objectId,
      "definition": {
        "name": { "en-US": object },
        "description": { "en-US": objectDescription },
        "type": activityType
      },
      "objectType": "Activity"
    }	
  };
  const result = ADL.XAPIWrapper.sendStatement(statement);
}

With this setup, we can send an xAPI statement by passing arguments to the function. Remember, though, that we must use quotes around each argument…the statement requires strings to work appropriately.

For example, we can send our example statement by calling the function like so using a Storyline “Execute JavaScript” trigger:

send_statement("completed", "http://activitystrea.ms/schema/1.0/complete", "Collecting Activity Info xAPI Tutorial", "https://www.devlinpeck.com/tutorials/acitvity-info-xapi-storyline", "Beginner xAPI Tutorial on devlinpeck.com", "http://activitystrea.ms/schema/1.0/article");

Voila! This line of code will effectively replace the variables in the statement with the arguments that you pass to it, and all you have to do is swap out those arguments when you want to send a new, completely different statement.

Publishing Your Project

The complete functionality will depend on you successfully publishing your project and modifying the published output as described in Part 3 of the Getting Started series.

You will also need to ensure that you’re collecting the user’s name and email address from the course if you want the code to work as shown.

If you’d rather use a filler name and email address to test your code, you can replace those variables with strings, like so:

const uNamejs = "testname";
const uEmailjs = "testemail@test.com";

Finally, if you haven’t configured your conf object to work with your LRS, then your statements will not send successfully. Return to Part 3 of the Getting Started series if you need additional help with this.

Conclusion

There you have it! You can now send an xAPI statement with an object that has supporting information and context, helping the LRS, analysts, and other developers make sense of what’s going on.

If you had any issues sending your statements, then you should refer to the common xAPI and Storyline troubleshooting steps.

Return to the Full Guide to xAPI and Storyline.