Main git repository with source code: https://gitlab.com/eduplex-api/user-behavior-tracker
Scope of this repository
The purpose of this project is to provide an alternative to google analytics using javascript to store information about pages and user interactions in the form of xAPI statements. Those xAPI statements can be stored in a xAPI compatible LRS like, for example, Learning Locker.
Required files
JsTracking.js
is the main file from this project , which exports all the functions needed to make the tracking work.verbs.js
is just a collection of verbs used in the xAPI statements to describe user actions.xapiwrapper.min.js
is a script injected automatically in the page head byJsTracking.js
. This is needed to create thewindow.ADL
object, which provide all the functions needed to store xAPI data in the LRS.
Configuration
You will need to set several configuration values in order to communicate with the LRS where the tracking data will be stored:
{
endpoint: BASE_URL + "edu/api/v1/xAPIProxy/",
user: userId,
password: userToken,
lmsHomePage: "https://proto.eduplex.eu"
}
We have implemented (in the same backend repository as the Module Occupation Matching) a proxy to forward xAPI requests from our backend to the LRS in order to keep credentials safe.
userId
: logged-in user ID.userToken
: logged-in user access token
How does it work
When you add this project as a package into your application you can access 2 modules:
jsTrackingFunctions
which contains all the functions you will need to call to track in your platform. Those functions are:initXapiTrack
– This function initializes the LRS and other configurations needed to start tracking. It is an asynchronous function, and it is needed to be called before any other tracking function. It receives one parameter, an object like the one in the section above with the lrs configuration.sendXapiStatement
– This one triggers a tracking event, which is stored in the LRS as a statement. It receives a parameter with the name of the event, for example:viewed
,progressed
,responded
,log in
…
verbsList
– This is a group of constants for the available verbs. For example if you want to trigger aviewed
event you can use the constantverbsList.viewed.key
to access the name of the event.
Some verbs have a callback function to process some extra data, for example when tracking a search
event you can include in the xAPI data what the user was looking for and even filters applied to the search. This callback function is optional but each verb will expect different objects in this function.
Before sending any tracking event the identifier of the logged-in user must be stored in window.JsTrackingUserLogged
.
Every statement saved in the LRS includes data from the current page. Also, if the page has structured data with https://schema.org/Course format in a application/ld+json
script all this data is stored inside the statement.
Example
In this example we are initializing the tracking and sending a viewed
event. This is storing an xAPI statement in the LRS for the user tracking-test-user
with the data from the current page.
window.JsTrackingUserLogged = 'tracking-test-user'
await jsTrackingFunctions.initXapiTrack({
endpoint: "https://lrs-eu-central-1.eduplex.eu/data/xAPI/",
user: "64b9634d139ecf5d77ec8bf335c582c8ee374f5",
password: "password1234",
lmsHomePage: "https://proto.eduplex.eu"
})
jsTrackingFunctions.sendXapiStatement(verbsList.viewed.key)
Also, an example of an event with custom options like the search
mentioned before:
const options = {
searchQuery,
filters
}
jsTrackingFunctions.sendXapiStatement(verbsList.viewed.key, options)
Where searchQuery
is a string and filters
is an object.
Example statement
{
"authority": {
"objectType": "Agent",
"name": "EduPLEx Client",
"mbox": "mailto:test@eduplex.eu"
},
"stored": "2022-09-26T12:06:08.158Z",
"context": {
"extensions": {
"https://proto.eduplex.eu/xapi/extension/device_token": "929f703a-9e42-4c44-897b-735bd1679aa8",
"https://proto.eduplex.eu/xapi/extension/course_data": {
"hasCourseInstance": {
"endDate": "2023-01-16T14:00:00+01:00",
"name": "A test course",
"startDate": "2022-08-15T01:05:00+02:00",
"location": {
"@type": "Place",
"address": {
"@type": "PostalAddress",
"addressCountry": {
"@type": "Country"
}
}
},
"offers": {
"@type": "Offer",
"url": "https://www.eduplex.eu/en/e/a-game-with-mushrooms-and-dinosaurs-24373/4989364bc6?c=166811",
"name": "A test course",
"availability": "InStock",
"price": 10,
"priceCurrency": "EUR",
"validFrom": "2022-06-19T00:00:00+02:00"
},
"@type": "CourseInstance",
"image": "https://eduplex.imgix.net/e/img/24373-a-game-with-mushrooms-and-dinosaurs-event_pic.jpeg?v=4&auto=compress&w=1920&h=606&fit=crop",
"description": "Learn the basics...",
"instructor": [],
"courseMode": "online"
},
"name": "A test course",
"timeRequired": "PT0H30M",
"url": "https://www.eduplex.eu/en/e/a-game-with-mushrooms-and-dinosaurs-24373/4989364bc6?c=166811",
"@context": "http://schema.org/",
"provider": {
"@id": "https://www.eduplex.eu/es/u/nordev",
"@type": "Organization",
"name": "Nordev",
"url": "https://www.eduplex.eu/es/u/nordev"
},
"isAccessibleForFree": "http://schema.org/False",
"@type": "Course",
"@id": "https://www.eduplex.eu/en/e/a-game-with-mushrooms-and-dinosaurs-24373/4989364bc6?c=166811",
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": 4.67,
"bestRating": 5,
"reviewCount": 3
},
"description": "Learn the basics...",
"inLanguage": "en"
}
}
},
"actor": {
"account": {
"homePage": "https://proto.eduplex.eu",
"name": "dani-test-tracking"
},
"objectType": "Agent"
},
"timestamp": "2022-09-26T12:06:08.158Z",
"version": "1.0.0",
"id": "3a2f0cb5-2ac5-4d17-a2ad-7704785c3d63",
"verb": {
"id": "http://id.tincanapi.com/verb/viewed",
"display": {
"en-US": "viewed"
}
},
"object": {
"id": "https://www.eduplex.eu/en/e/a-game-with-mushrooms-and-dinosaurs-24373/4989364bc6?c=166811",
"definition": {
"type": "http://adlnet.gov/expapi/activities/course"
},
"objectType": "Activity"
}
}
Other considerations
In projects with webpack and babel we found a recurring error with optional chaining operator (?.
), it shows something like this in build time: Module parse failed: Unexpected token You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file.
It can be fixed by adding this plugin to your webpack/babel config:
'@babel/plugin-proposal-optional-chaining'
Also make sure webpack transpile our code, in a nuxt project this is as simple as adding the next line inside the build
property in nuxt.config.js
.
transpile: ['user-behavior-tracker']
License
The source code for the site is licensed under the MIT license, which you can find in the LICENSE file.
xapiwrapper.min.js
file has been taken from another open source repository (https://github.com/adlnet/xAPIWrapper) which is under Apache License