I have a script that is 90% complete and working. I'm using the older code for getting LRS statements. It is the one that uses tincan.js. I am trying to pull down and parse the following: Actor, verb, object, timestamp and the id under contextActivities. It is the last one that is giving me fits. Could you please look at my last catch/try statement and tell me where I'm going wrong? Thank you.
<!DOCTYPE html>
<!--Parsing "resumed" statements-->
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Get my Statements</title>
<script type="text/javascript" src="build/tincan.js"></script>
</head>
<body>
<h1>Get statements 101</h1>
<div id='response'></div>
<script>
var lrs;
try {
lrs = new TinCan.LRS(
{
endpoint: "https://lrs.adlnet.gov/xapi/",
username: "xapi-tools",
password: "xapi-tools",
allowFail: false
}
);
}
catch (ex) {
console.log("Failed to setup LRS object: " + ex);
// TODO: do something with error, can't communicate with LRS
}
//Pulls all of the resumed statements
lrs.queryStatements(
{
params: {
verb: new TinCan.Verb(
{
id: "http://adlnet.gov/expapi/verbs/completed"
}
),
since: "2016-01-05T08:34:16Z"
},
callback: function (err, sr) {
if (err !== null) {
console.log("Failed to query statements: " + err);
// TODO: do something with error, didn't get statements
return;
}
if (sr.more !== null) {
// TODO: additional page(s) of statements should be fetched
}
var container = document.getElementById('response');
//container.innerHTML = (err !== null ? 'ERROR' : JSON.stringify(sr.statements));
container.innerHTML = (err !== null ? 'ERROR' : parseMyData(sr));
}
}
);
parseMyData = function(result) {
var statements = result.statements;
var output = '';
var name,verb,activity, timestamp, context;
for(var i=0;i<statements.length;i++){
// check the statement for a usable name value
// (priority = actor.name, actor.mbox, actor.account.name)
if(statements[i].actor.name != null && statements[i].actor.name != "") {
name = statements[i].actor.name
}else if(statements[i].actor.mbox != null && statements[i].actor.mbox != "") {
name = statements[i].actor.mbox
}else{
name = statements[i].actor.account.name
}
// check the statement for a usable verb value
// (priority = verb.display['en-US'], verb.id)
try{
verb = statements[i].verb.display['en-US'];
}catch(e){
verb = statements[i].verb.id;
}
// check the activity for a usable value
// (priority = definition.name['en-US'], id)
try{
activity = statements[i].target.definition.name['en-US'];
}catch(e){
activity = statements[i].target.id;
}
try{
timestamp = statements[i].timestamp;
}catch(e){
timestamp = statements[i].timestamp;
}
try{
context = statements[i].target.contextActivities.other['id'];
}catch(e){
context = statements[i].activity.other['id'];
}
output += name + ' - ' +
verb + ' - ' +
activity + ' - ' +
timestamp + ' - ' +
context +
'<br>'
}
return output;
}
</script>
</body>
</html>
You are trying to access a property that will never exist:
statements[i].target.contextActivities
The last portion of that will exist in statements[i].context
when it exists, which is not always unless you know the statement ahead of time. Pretty much all of the properties should be checked for null
before accessing them, doing so will allow you to remove your try/catch blocks. The property names themselves are very stable and the library explicitly assigns null
values to known properties. Additionally the other
property will hold an array of Activities (when it is populated) so you should access them by index, so contextActivities.other[0].id
etc.
For the target
property you should check that it is an instance of a particular class as it can contain multiple types of objects (so can actor
but they effectively match). In general for this specific task you may also want to leverage the toString
methods where possible, they do very similar work.