Use JavaScript to send data to LMS with SCORM 2004 4th edition
Jun 05, 2021
Hi all,
There are many discussions on this topic but I haven't seen a full script using SCORM 2004 so I thought I could share what I've used to report data that includes detailed question text and user responses back to LMS with SCORM 2004.
For this code, I used in Workday Learning LMS but I think it should work on other LMSs. The code below is just showing the basic to report data back to LMS. So you will most likely use variables to feed cmi.interactions.
// For SCORM2004 only
var player = GetPlayer(); //talk to Storyline
function findLMSAPI(win) {
if (win.hasOwnProperty("GetStudentID")) return win;
else if (win.parent == win) return null;
else return findLMSAPI(win.parent);
}
var lmsAPI = findLMSAPI(this);
//These lines will be used to set score value in SCORM2004
var finalRawScore = player.GetVar("zScore");
var passPercent = player.GetVar("zPassingPercentage");
var scoreScale = finalRawScore/100;
//Set score value here. These 4 lines are necessary for SCORM2004
SCORM2004_objAPI.SetValue('cmi.score.scaled', scoreScale);
SCORM2004_objAPI.SetValue('cmi.score.raw', finalRawScore);
SCORM2004_objAPI.SetValue('cmi.score.min', '0');
SCORM2004_objAPI.SetValue('cmi.score.max', '100');
//Here is the code to report question text and user responses back to LMS.
//These fields were tested in Workday Learning LMS and were necessary for reporting.
//You can also use variables to feed cmi.interactions. The 0 here means the first record.
SCORM2004_objAPI.SetValue('cmi.interactions.0.id', '001');
SCORM2004_objAPI.SetValue('cmi.interactions.0.description', 'question 1 text here');
SCORM2004_objAPI.SetValue('cmi.interactions.0.objectives.0.id', '001');
SCORM2004_objAPI.SetValue('cmi.interactions.0.type', 'fill-in'); //refer to note1 below
SCORM2004_objAPI.SetValue('cmi.interactions.0.timestamp', '2020-12-23T10:00:00');
SCORM2004_objAPI.SetValue('cmi.interactions.0.learner_response', 'This_is_a_TEST_response');
SCORM2004_objAPI.SetValue('cmi.interactions.0.result', 'neutral'); //refer to note2 below
//note1 (“true-false”, “choice”, “fill-in”, “long-fill-in”, “matching”, “performance”, “sequencing”, “likert”, “numeric” or “other”, RW) Which type of interaction is recorded
//note2 “correct”, “incorrect”, “unanticipated”, “neutral”
//determine if the user passed or failed. And if you would like the course to mark as completed or not if the user failed.
if (SCORM2004_objAPI.GetValue('cmi.score.raw') >= passPercent)
{
SCORM2004_objAPI.SetValue("cmi.success_status","passed");
SCORM2004_objAPI.SetValue("cmi.completion_status","completed");
}
else
{
SCORM2004_objAPI.SetValue("cmi.success_status","failed");
SCORM2004_objAPI.SetValue("cmi.completion_status","incomplete");
//can change to completed if you want the course to be marked as completed even if the user failed.
}
Here is an example of using the Storyline variables to feed cmi.interactions.
var Q1_ID = player.GetVar("Q1_ID");
var Q1_Description = player.GetVar("Q1_QuestionDescription");
var Q1_Response = player.GetVar("Q1_Response");
var Q1_Result = player.GetVar("Q1_Result");
var Q1_Timestamp = player.GetVar("Q1_Timestamp");
SCORM2004_objAPI.SetValue('cmi.interactions.0.id', Q1_ID);
SCORM2004_objAPI.SetValue('cmi.interactions.0.description', Q1_Description);
SCORM2004_objAPI.SetValue('cmi.interactions.0.objectives.0.id', Q1_ID);
SCORM2004_objAPI.SetValue('cmi.interactions.0.type', 'fill-in');
SCORM2004_objAPI.SetValue('cmi.interactions.0.timestamp', Q1_Timestamp);
SCORM2004_objAPI.SetValue('cmi.interactions.0.learner_response', Q1_Response);
SCORM2004_objAPI.SetValue('cmi.interactions.0.result', Q1_Result);
Note that the timestamp format would need to be like this: '2020-12-23T10:00:00'. So when you set up the Storyline variables to store timestamp, you would need to use JavaScript to convert current time. Here is the code to convert the time to this format.
//convert local time to ISO format and remove millisecond with slice
var date = new Date();
var JStimestamp = new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(0,-5);
player.SetVar("Q1_Timestamp", JStimestamp); //send value back to the Storyline variable.
For the complete list of SCORM 2004 runtime reference, visit:
https://scorm.com/scorm-explained/technical-scorm/run-time/run-time-reference/
16 Replies
Thanks for posting this. I have taken a variation of your code and so far it is working on SCORM Cloud. Now for the test... to work on Oracle Taleo. :)
@Carrie Thanks for posting this, really helpful!
Just checking, how would you implement this with something like Likert? I can see that there's a
cmi.interactions.0.type
for it but what would be the format for the actualcmi.interactions.0.learner_response
?Do you just have to combine the responses into one long string? I assume you can't send an array in
cmi.interactions.0.learner_response
?Thanks again for the code!
Also, is there a reason the script discovers the LMS API if the reference is not then being used?
@PJ, I am so sorry that it takes me so long to respond as I am not following the thread.
I never used Likert type with this code before as I discovered the default Likert responses were not reader-friendly in Workday Learning. So in a situation like this, I built my own Likert and stored information and used fill-in type.
You can use array in the code. When you use array, you will end up having multiple "rows" in your final report. See partial code below using for loop with array:
(forgive me for my sloppy coding. I didn't start var i with 0 because I had the question# setup starting with 1 in SL and didn't want to go back to change all the variables. lol)
In this case, you will generate a new row for each question.
@PJ, I copied the first segment of the code from other discussions, so I probably cannot answer you on this one. From what I saw in other discussions, it seems like
var lmsAPI = findLMSAPI(this)
is used to communicate with LMS. But I could be wrong so if anyone can help out with this question, it will be much appreciated!The findLMSAPI function examines its parent iframe or window and determines if the API is located there. If it does not, it recursively tries to keep on going to its other parents until it finds the API. Once it does, you are able to communicate with the LMS.
Replying so i can find this post again when needed :-)
Good call, Math! :)
Hope that you are all having a great week!
😂😂😂
Will this work for storyline blocks inside Rise 360?
And can you add this sort of thing somehow to non-storyline blocks somehow? (ie. reporting on videos, other interactives like drag and drop)
Unfortunately no. Storyline files are used to display non-trackable activities. To do that would imply a lot of JavaScript tinkering to communicate with the LMS which Articulate would probably say that this functionality is out of scope.
I tried to get the variables for the cmi interactions but I always get a null value. in particular I need to have the value of these two elements.
var Q1_Description = player.GetVar("Q1_QuestionDescription");
var Q1_Response = player.GetVar("Q1_Response");
Am I doing something wrong? I attach the story file.
First thing to do whenever something isnot working... Open up your Inspector tools ( F12 or CTRL+Shift+I ) in your browser. This is what you see then.
When working with Javascript in Storyline the console window on the right is critical because here you see all errors.
And immediately you notice that all your variables cannot be resolved.
Path did not resolve at: Q1_Response
This means Storyline cannot find that variable. Either it is misspelled or not available.
So the next step is checking you Storyline. And opening up the variable window the first thing you see... none of these variables exist... so the error is clear :-)
Now you know how to check this...and how to fix it :-)
Kind regards,
Math
Thank you very much for your answer but my aim was to have the value of the student's answer and the description of the question, without having to insert it as a variable. It's possible?
You always need variables. Either the default Storyline ones or custom ones.
My Workday Systems Admin is saying I need to have something enabled in my courses in order for Workday to report question level data. Does anyone know if there is a specific setting, or working with the JavaScript coding is the only way to make this happen?
Thanks!