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.
Feel free to ask for help in the eLearning Development space in the ID community (free for mailing list subscribers).
First thing's first — what's an activity? In our previous tutorials, we discussed xAPI objects and 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:
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.
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:
{% c-block language="js" %}
"object": {
"id": objectId,
"definition": {
"name": { "en-US": object }
}
}
{% c-block-end %}
(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:
{% c-block language="js" %}
"object": {
"id": objectId,
"definition": {
"name": { "en-US": object }
},
"objectType": "Activity"
}
{% c-block-end %}
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 be useful when it comes time to query statements from the LRS.
At the moment, all we know about the object is that it's an activity. We can also pass it an ID and "name" using the sendStatement() function's parameters (See Part 3 of the Getting Started series if you're not familiar with parameters).
However, what if we want to provide additional information about the activity?
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:
{% c-block language="js" %}
"object": {
"id": objectId,
"definition": {
"name": { "en-US": object },
"description": { "en-US": "Beginner xAPI Tutorial on devlinpeck.com" }
},
"objectType": "Activity"
}
{% c-block-end %}
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.
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:
{% c-block language="js" %}
"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"
}
{% c-block-end %}
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 ADL's xAPI Vocab Server.
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.
Now we'll take a look at our sendStatement() 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 need to add parameters to ensure that we 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 sendStatement() function should now look like this:
Note that you may need to scroll horizontally to see all of the code in the code box.
{% c-block language="js" %}
function sendStatement(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);
}
{% c-block-end %}
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:
{% c-block language="js" %}
sendStatement("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");
{% c-block-end %}
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.
The complete functionality will depend on you successfully publishing your project and modifying the published outputas described in Part 3 of the Getting Started series. You will need to add the xapi-statements.js and xapiwrapper.min.js files every time you publish your Storyline course.
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:
{% c-block language="js" %}
const uNamejs = "testname";
const uEmailjs = "testemail@test.com";
{% c-block-end %}
Finally, if you haven't configured your "conf" object to work with your LRS, then your statements will not send successfully. See here if you need additional help setting the conf object.
There you have it! You can now send an xAPI statement with an object that has supporting information and context. This helps everyone working with the data make better 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.