Send usage data to Google Analytics via xAPI
Understanding how people use your content is important, and maybe even more important for interactive content. We have to understand how people interact with the content in order to know how we can improve it and what types of content we should focus on.
Together with NDLA we have looked into how H5P can report user data into Google Analytics. The problem NDLA faces is that they use Google Analytics as a tool to improve content, but via Google Analytics they don’t get much information about how their users interact with their H5P content.
H5P already reports what the user does within the H5Ps as xAPI statements. It was natural to think that these statements could be sent to Google Analytics, so we created a prototype that turns xAPI statements generated by H5P into Google Analytics events. We have the prototype installed on H5P.org, and in Google Analytics we can now for instance see that:
- on October 12th, the multichoice questions were answered 665 times, 179 of the answers came from the UK and 170 from the US.
- 561 of the multichoice answers on October 12th came from the multichoice questions within the October release note, and the average score from the release note was 39.93 % for these two questions combined.
- in this Course Presentation example, people have changed slides 10 304 times and completed the presentation 1 293 timers the last month. See the image below:
Google analytics visualizes the data from H5P in a great way, and it's also able to combine the xAPI data with the data Google Analytics normally gathers so that you may gain really interesting insights.
More examples
An interesting insight we gained from the data from the October Release Note was that the Course Presentation we used to visualize how we've improved the selection of button vs poster embedding of elements in Interactive Videos had very low engagement compared to other interactivities on the page. See the Course Presentation below:
From this, we learned that it probably wasn't clear enough how the user could interact with this content, and we'll have to find a way to improve this the next time we do something similar.
Google Analytics can also be used to see how well users perform on different tests, and also to see if people in different regions perform differently. From the following Interactive Video, we can see that 35.85 % of Norwegians knew that Sushi actually doesn’t mean raw fish, and the average score was 28.23 %. Norway was only beaten by Japan:
We find our prototype very useful on H5P.org, and we'll probably create a module for integrating H5P with Google Analytics pretty soon.
The technical details
So how do we do this? For those of you not familiar with xAPI, xAPI in this context is pretty much statements about what a user is doing. For instance
"User answered the test 'Can Pluto fly?'"
or
"User changed from slide 3 to slide 6 in the presentation 'Who is Pluto?'"
Statements consists of an Actor(the user), a verb ('answered' for instance) and an Object (the presentation "Who is Pluto" for instance). H5P generates statements like this all the time.
What we've done is made a separate peace of software that turn these statements into Google Analytics events. Google Analytics events have several properties, and this is how we've mapped them:
- Category: Here we use the content type that sends the statement, e.g. H5P.MultiChoice
- Action: Here we use the xAPI verb, e.g. "Answered"
- Opt_label: The H5P's title
- Opt_value: The result if a result exists or which slide a user jumped to if it was a progressed event.
For those of you who are eager to try this out on your own site here is the unpolished prototype code:
/** * Add scripts to h5ps * * @param array $scripts * Array of objects with properties path and version. Version is on the form * ?ver=1.0.2 and is used as a cache buster * @param array $libraries * Array of libraries indexed by the library's machineName and with an array * as value. The value has the properties majorVersion and minorVersion * @param string $mode * What mode are we in? Possible values are "editor", "div", "iframe" and "external" */ function hook_h5p_scripts_alter(&$scripts, $libraries, $mode) { $scripts[] = (object) array( // Path relative to drupal root 'path' => drupal_get_path('module', 'h5p_ga') . '/h5p-ga.js', // Cache buster 'version' => '?ver=0.0.1', ); }
And the javascript:
(function () { // Improve performance by mapping IDs var subContentIdToLibraryMap = {}; /** * Look through params to find library name. * * @private * @param {number} id * @param {object} params */ function findSubContentLibrary(id, params) { for (var prop in params) { if (!params.hasOwnProperty(prop)) { continue; } if (prop === 'subContentId' && params[prop] === id) { return params.library; // Found it } else if (typeof params[prop] === 'object') { // Look in next level var result = findSubContentLibrary(id, params[prop]); if (result) { return result; } } } } if (window.H5P) { H5P.jQuery(window).on('ready', function () { H5P.externalDispatcher.on('xAPI', function (event) { try { if (!window.parent.ga) { return; } // First we need to find the category. var category; // Determine content IDs var contentId = event.data.statement.object.definition.extensions['http://h5p.org/x-api/h5p-local-content-id']; var subContentId = event.data.statement.object.definition.extensions['http://h5p.org/x-api/h5p-subContentId']; if (subContentId) { if (subContentIdToLibraryMap[subContentId]) { // Fetch from cache category = subContentIdToLibraryMap[subContentId]; } else { // Find category = findSubContentLibrary(subContentId, JSON.parse(H5PIntegration.contents['cid-' + contentId].jsonContent)); if (!category) { return; // No need to continue // TODO: Remember that it wasnt found? } // Remember for next time subContentIdToLibraryMap[subContentId] = category; } } else { // Use main content library category = H5PIntegration.contents['cid-' + contentId].library; } // Strip version number category = category.split(' ', 2)[0]; // Next we need to determine the action. var action = event.getVerb(); // Now we need to find an unique label var label = event.data.statement.object.definition.name['en-US']; // Title // Add contentID to make it eaiser to find label += ' (' + contentId; if (subContentId) { label += ' ' + subContentId; } label += ')'; // Find value var value; // Use result if possible var result = event.data.statement.result; if (result) { // Calculate percentage value = result.score.raw / ((result.score.max - result.score.min) / 100); } // ... or slide number if (action === 'Progressed') { var progress = event.data.statement.object.definition.extensions['http://id.tincanapi.com/extension/ending-point']; if (progress) { value = progress; } } // Validate number value = Number(value); if (isNaN(value)) { value = undefined; } window.parent.ga('send', 'event', category, action, label, value); } catch (err) { // No error handling } }); }); } })();
We appreciate you taking some time to share your feedback.
Comments
dtopps
Wed, 03/02/2016 - 21:04
Permalink
xAPI from H5Ps
We have been trying to figure out the same issue for our own open-source education software, OpenLabyrinth. (http://openlabyrinth.ca/)
We really like xAPI for being able to track what our users are doing with our cases. So coming across the really useful stuff you guys are doing with xAPI is really exciting. And the link to Google Analytics is most encouraging, since we would love to know what our users are doing with our embedded videos.
falcon
Thu, 03/03/2016 - 15:59
Permalink
I'm glad you like it :) We
arvin.origenes
Fri, 06/11/2021 - 16:35
Permalink
xapi to google analytics
Hello, is there any step by step how you apply this code? thank you
rmuhammad517
Mon, 10/10/2016 - 17:09
Permalink
Does this work in Wordpress?
I noticed the "drupal_get_path" command. Does this work in Wordpress? If s, what is the equivalent command?
Thanks,
fnoks
Tue, 10/11/2016 - 10:22
Permalink
Hi,You should look here:
Hi,
You should look here: https://github.com/h5p/h5pmods-wordpress-plugin/blob/master/h5pmods.php
roblespar
Thu, 10/13/2016 - 04:35
Permalink
xAPI to Google Analytics
This is a great development! If you can roll this out for all platforms (Drupal, Moodle, WordPress etc) I am sure there would be considerable uptake by educational institutions to measure the effectiveness of H5P activities compared to other parts of the LMS. Well done!
timothylim23
Thu, 10/13/2016 - 09:52
Permalink
Hi, we are hoping that
Hi,
we are hoping that developers from the community will pitch in and contribute code to help with other integrations. For example we are working on incorporating a moodle and xAPI intergration developed by the community:
https://github.com/h5p/h5p-moodle-plugin/pull/114
Hopefully more will come!
louise.bennett@...
Wed, 04/05/2017 - 13:02
Permalink
Not working on embed
Hi,
This is great - very useful. I'm having some issues with embedded content, though. It works fine when I look directly at the embed page, but not when I actually embed it into another page. Any ideas on what might be going wrong, and how I might fix it?
Thank you!
Louise
tim
Thu, 04/06/2017 - 10:04
Permalink
Hi Louise, could you describe
Hi Louise, could you describe the problem in more detail and attach a screenshot if necessary?
VuHoang
Tue, 05/30/2017 - 10:14
Permalink
Hi Tim
Hi Tim,
I am looking forward more information about h5p to analytics.
tim
Fri, 06/02/2017 - 10:20
Permalink
Hi VuHoang, what else would
Hi VuHoang,
what else would you like to learn about? There is also some information here about anlayzing results in H5P.
VuHoang
Tue, 06/20/2017 - 06:41
Permalink
I'm using Canvas
I want to know how I can use your h5p google analytics code for Canvaslms.
tomaj
Wed, 06/21/2017 - 08:13
Permalink
Documentation
There is some documentation on it here.
But it will become more streamlined and easy to use when h5p.com is out.
- Tom
stopbit
Mon, 07/30/2018 - 13:12
Permalink
Cookie Choice
Hi,
How would you handle the 'cookie choice' required by the EU when using Google analytics and H5P?
I'm using WordPress and Moodle and would like to explore the options - As it could be construed that analytics are required as built-in technical functionality of the software (and if we choose to anonymise users), would a simple notice suffice or should we come up with a mechanism, so the user can make a choice to accept/deny google cookies.
Thanks
otacke
Mon, 08/06/2018 - 16:57
Permalink
Hi stopbit!That's a question
Hi stopbit!
That's a question for lawyers and judges, I fear.
Best,
Oliver
jmalteschuldt
Wed, 03/25/2020 - 10:36
Permalink
Google analytics xAPI
Hey everyone,
I just wanted to ask, if there is a step by step guide on how to do this on my wordpress site? How to use
Xapi statements with a google analytics plugin?
Kind regards
Malte