Course status = completed

492 views
Skip to first unread message

David

unread,
Feb 24, 2011, 10:13:44 AM2/24/11
to eLearning Technology and Development
I’m having a great deal of trouble to get an e-learning course to sing
with SCORM.

In the last minute a request for report of course progress and
completion was raised. The SCO is made in Flash and do contain ten
chapters.
I need some help with how this is made. Every chapter reports when
they're finished, and when they all are, the completion status is set
to finished for the course.

I'll supply a theoretical model on how I think I'll work it out, but
feel free to suggest something completely different, if you think I'm
heading down the wrong path.



In theory:

Every chapter will get a string looking something like
"0,1,0,0,0,0,1,1,0,0", where 1 means finished and 0 means incomplete.
I assume 'LMSGetValue' would be the proper way to do this? Lets say I
just finished chapter 1, I have to change the first number in the
string to "1". Updating the string from:
"0,1,0,0,0,0,1,1,0,0" to
"1,1,0,0,0,0,1,1,0,0".

Every time I write a "1" to the string, telling it that a certain
chapter is finished, I need to check the string if it's reporting
finshed ("1,1,1,1,1,1,1,1,1,1") or not. If all courses are set to
passed/completed/finished, whatever the terminology is, it should send
a 'cmi.core.completion_status = complete' to the LMS.



In practice:

import flash.external,*;
String(ExternalInterface.call("LMSGetValue, "cmi.suspend_data,
theCourseStatus"));

String(ExternalInterface.call("LMSSetValue, //..add the number "1" to
the chapters specific place in the string..//

if
//..when this number is added to the string makes the string contain
all completed chapters "1,1,1,1,1,1,1,1,1,1"..//

then
//..cmi.completion_status, "completed"..//

else
//..cmi.completion_status, "incomplete"..//

String(ExternalInterface.call("LMSCommit", ""));
stop();

John Campbell

unread,
Feb 24, 2011, 11:45:21 AM2/24/11
to elearning-technolo...@googlegroups.com
You seem to be on the right track. You can store whatever you need to
save the state of your content in cmi.suspend_data. You also need to
set cmi.exit = "suspend" for the LMS to know that you want to "resume"
on the following launch of the SCO. If it does not "resume" ( you can
check the value of cmi.entry I believe), then the data is not
guaranteed to be there. In fact, it's probably only compliant if it's
wiped clean.

You also need to call terminate at some point. Your API call may be
LMSFinish() or something like that. The name varies.

I am in the minority here probably when I say that it's not a great
idea to call the API directly from Flash. It just makes it harder to
update versions of SCORM or use your content outside of SCORM. But
most people do it. I choose an approach similar to Articulate, which
provides an abstraction above the apiwrapper. This way, if you go
from 1.2 -> 2004 -> AICCC -> normal web delivery, it's just a matter
of swapping out JS files or toggling some variables. No need to
re-export the SWFs.

jpc

> --
> You received this message because you are subscribed to the Google Groups "eLearning Technology and Development" group.
> To post to this group, send email to elearning-technolo...@googlegroups.com.
> To unsubscribe from this group, send email to elearning-technology-and...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/elearning-technology-and-development?hl=en.
>
>

--
John Campbell
(713) 364-4323

Matt Perkins

unread,
Feb 24, 2011, 12:02:53 PM2/24/11
to elearning-technolo...@googlegroups.com
Agree w/ John here.

But it looks like you're getting/setting the suspend_data variable frequently? You should get it when the course starts and set it when it ends (or on every chapter completion). Keep the data in a variable or state variable for each chapter. Update the variable and then send that variable to the suspend data.

John - as another approach:
I've written an LMS protocol facade interface that abstracts SCORM 1.2/2004 and AICC HACP calls. In my course setup XML, i specify the protocol and have a little factory that returns the correct protocol. The Flash just talks to the protocol in abstract terms and it's transparent to the course. I don't like Javascript! :) 


Matt Perkins
---------------------------------
http://www.nudoru.com
http://udon.nudoru.com

John Campbell

unread,
Feb 24, 2011, 12:15:23 PM2/24/11
to elearning-technolo...@googlegroups.com, Matt Perkins
> John - as another approach:
> I've written an LMS protocol facade interface that abstracts SCORM 1.2/2004
> and AICC HACP calls. In my course setup XML, i specify the protocol and have
> a little factory that returns the correct protocol. The Flash just talks to
> the protocol in abstract terms and it's transparent to the course. I don't
> like Javascript! :)

Hey, whatever works! I just don't like having to open a tool to
modify content when it's easily done JS with MacVim. :-) This way,
when whatever they do with CMI-5/SCORM comes around, it's not going to
be hard to reuse and edit with easy fixes. I don't own Flash, nor
care to.

jpc

sharon goodall

unread,
Feb 24, 2011, 12:26:56 PM2/24/11
to elearning-technolo...@googlegroups.com
Curious: what's "CMI-5/SCORM"?

thanks,
sharon


--

Philip Hutchison

unread,
Feb 24, 2011, 12:29:11 PM2/24/11
to elearning-technolo...@googlegroups.com, sharon goodall
Hi David

If you use the pipwerks SCORM wrapper, cmi.exit is handled for you automatically.  The course is also automatically set to incomplete for you unless you specify otherwise.  The wrapper is SCORM version agnostic.

Your code would become something like this:

import com.pipwerks.SCORM;

var connected:Boolean = false;
var suspend_data_string:String;
var sco:SCORM = new SCORM();

connected = sco.connect();  
//initializes the SCORM connection between the course and LMS

if(connected){

suspend_data_string = sco.get("cmi.suspend_data");
//returns whatever you saved in the last launch
//such as "0,1,0,0,0,0,1,1,0,0"

//then handle your suspend_data_string using your course's internal logic

//when you're ready to send updated completion data to the server:
sco.set("cmi.suspend_data", suspend_data_string);

//set course status, if needed
sco.set("cmi.lesson_status", "completed");

//tell the LMS to persist anything you've sent:
sco.save();

//Make sure you properly terminate your connection with the server:
sco.disconnect();

}

stop();

----------

Of course this is a very simplified version.  You'd also need to ensure your course doesn't invoke connect() or disconnect() more than once.


John: 

Regarding your point about not having your SCORM code in your course, I agree on principle, which is why most of the pipwerks wrapper's logic is contained in the accompanying JavaScript file. However, you advocate complete removal of SCORM calls from a course, which I admit the pipwerks wrapper doesn't do. You're right that in the long run, it's a cleaner approach, but there are flipsides:

* SCORM versions change so infrequently that by the time an update is rolled out (and -- more importantly -- supported by your LMS) the course will be YEARS old and probably irrelevant or due for an overhaul anyway. As you know, SCORM is not scheduled to have any major updates for at least a few more years, and in all likelihood the next SCORM edition will be a complete break from the past.

* For most people, if the course is a one-time deal or if they're in a hurry, it probably isn't worth the overhead of building an abstraction layer.

Again, I agree with you on principle, but abstracting on the level you suggest is best suited for people who build courses that need to work with multiple standards (SCORM 1.2, SCORM 2004, AICC, etc.) and can quickly be flipped from one standard to another by setting an option in a JavaScript-based config file.

Unless, of course, someone builds that abstraction layer as an open-source solution free for everyone else to use. :)

Sharon: CMI 5 is the latest round of discussions aimed at expanding the CMI data model, which is used by both SCORM and AICC.

- philip

Estes Ethan

unread,
Feb 24, 2011, 12:51:27 PM2/24/11
to elearning-technolo...@googlegroups.com
David - one thing to keep in mind is that if you want to keep multiple values in cmi.suspend_data you'll need to concatenate them together with a delimiter so you can separate the string on re-entry. 

"0,1,0,0,0,0,1,1,0,0&var1=blah&var2=blahblah" etc.


Also I url encode the concatenated string to avoid any issues with unique characters.


-EÆ


Philip Hutchison

unread,
Feb 24, 2011, 12:59:30 PM2/24/11
to elearning-technolo...@googlegroups.com, Estes Ethan
I just realized I made a typo:

cmi.core.lesson_status for SCORM 1.2
cmi.completion_status and cmi.success_status for SCORM 2004

John Campbell

unread,
Feb 24, 2011, 2:46:30 PM2/24/11
to elearning-technolo...@googlegroups.com
> * SCORM versions change so infrequently that by the time an update is rolled
> out (and -- more importantly -- supported by your LMS) the course will be
> YEARS old and probably irrelevant or due for an overhaul anyway. As you
> know, SCORM is not scheduled to have any major updates for at least a few
> more years, and in all likelihood the next SCORM edition will be a complete
> break from the past.

Yeah, I always try and preface things with the fact that I'm a bit
different than many cases.

The de-coupling from standards is one benefit. The main one I have
actually taken advantage of myself is that it's easier to provide a
Flash dev with a clean API than to expose them to the cmi based API
wrapper. It's also easier to do different things with the data after
the production team is done with it and moved on. In my company, we
had about 7-10 Flash devs. Not one knew anything about SCORM, nor did
they want to. And they certainly did not need to. The API I provided
them took very little ramp up time as it was generic and made more
sense to the newbie than the cmi.data model related API. It's also
much easier to maintain. Even if you don't like JS, it's easier to
edit and test on an LMS.

Also, the insanity I see in CP courses with the SCORM API being
blasted from all directions makes me hate how the tight coupling leads
to problematic courseware, especially for 2004. I think the
Articulate approach (done by Rustici) validates that architecturally
it's more sound. Adobe, in my opinion, has completely missed the mark
with regards to SCORM 2004. It seems they did "just enough" to be
somewhat compliant, but had little regard for functionality. The fact
that you CAN'T create a SCORM 2004 multi-SCO sequenced course without
hacking the JS traffic, is disheartening. Maybe that was too blanket
a statement, but it's close enough.

> * For most people, if the course is a one-time deal or if they're in a
> hurry, it probably isn't worth the overhead of building an abstraction
> layer.

I would not do it, if it's one layer per course. I used a single HTML
file and JS library set for the last 2 years of delivering SCORM 2004.
It's a one time deal.

> Again, I agree with you on principle, but abstracting on the level you
> suggest is best suited for people who build courses that need to work with
> multiple standards (SCORM 1.2, SCORM 2004, AICC, etc.) and can quickly be
> flipped from one standard to another by setting an option in a
> JavaScript-based config file.

Again, I use that as an example benefit. I have only delivered in
2004 to be honest.

> Unless, of course, someone builds that abstraction layer as an open-source
> solution free for everyone else to use. :)

It's more conceptual than anything. I just developed a generic
scoring object and API in terms that our devs were more familiar wtih.
It evolved over the first few years and then was almost bulletproof
for the final 2-3.

jpc

John Campbell

unread,
Feb 24, 2011, 2:47:56 PM2/24/11
to elearning-technolo...@googlegroups.com, Estes Ethan
A very good way to store suspend data is JSON. It's a perfect fit
for both JS and AS objects. I would suggest just stringifying your
AS object directly to suspend_data and parsing it on entry. 1 line of
code each direction.

jpc

--
John Campbell
(713) 364-4323

Ridgie Barton

unread,
Feb 24, 2011, 3:01:52 PM2/24/11
to elearning-technolo...@googlegroups.com, John Campbell, Estes Ethan
We use JASON, but FYI - some LMS's don't handle all characters - We had to Escape out ] on a Docent LMS.

Ridgie Barton
piXvfm




Matt Perkins

unread,
Feb 24, 2011, 3:09:19 PM2/24/11
to elearning-technolo...@googlegroups.com
Lately I've been using XML and in Flash, compressing it with a Base64 encoding to remove some of the bulk.. It's working fine on our LMS so far. But JSON is probably easier :)

We had a need to save some data to a place where it could be seen by a learner (a SharePoint list) and I came up with this trick to encode it so they couldn't see the saved data. Just in case.


Matt Perkins
---------------------------------
http://www.nudoru.com
http://udon.nudoru.com


John Campbell

unread,
Feb 24, 2011, 3:11:12 PM2/24/11
to elearning-technolo...@googlegroups.com, Ridgie Barton, Estes Ethan
Good point, a couple of times we had a glitchy character. But that
happens regardless of the format you store things in. Those are
always hard to debug too. But overall, JSON is awesome and nearly an
industry standard for web data exchange.

--
John Campbell
(713) 364-4323

Ridgie Barton

unread,
Feb 24, 2011, 3:20:56 PM2/24/11
to j...@alumni.rice.edu, elearning-technolo...@googlegroups.com, Estes Ethan
We made a debugger that allowed us to send and get SCORM commands and data to see what broke the LMS. Easy to do & saved us a lot of headaches when things weren't working right.

Ridgie Barton
piXvfm


John Campbell

unread,
Feb 24, 2011, 3:23:56 PM2/24/11
to elearning-technolo...@googlegroups.com, Ridgie Barton, Estes Ethan
Cool. What I hate are the "invalid character on line 1" type errors.
And line1 is 64,000 characters. :-)

jpc

--
John Campbell
(713) 364-4323

John Campbell

unread,
Feb 24, 2011, 3:27:10 PM2/24/11
to elearning-technolo...@googlegroups.com
Hey, Phil, I just saw your tweet. Thanks for the plug. I may blog
myself about it.

http://pipwerks.com/2011/02/24/abstracting-your-courses-tracking-code/

If you are interested, I have no issue sharing my framework with you.
I'd probably have to conceptually re-write it for IP reasons before we
released it. But I am about to do that anyway for an upcoming
project. SInce it is an evolution of code, some refactoring is
probably needed. Also, it was for our needs, so it may need more
breadth.

jpc

--
John Campbell
(713) 364-4323

sharon goodall

unread,
Feb 24, 2011, 7:41:13 PM2/24/11
to elearning-technolo...@googlegroups.com
Thanks, Philip, for the explanation.

John: your interface sounds great, especially the ability to hide the
scorm details from the flash developers. As a flash developer, I would
love to have something like that!

Sharon

> John Campbell
> (713) 364-4323

David

unread,
Mar 31, 2011, 3:01:44 AM3/31/11
to eLearning Technology and Development
Thank you all for your input!

I have to look into the pipwerks wrapper, atm I am using APIWrapper.
Also, I now know that it has to conform to scorm 1.2 (so if you know
some of the things I'm trying to use, please gimme a hint)

I have some questions, the theory for the updaterd code looks like
this, and it's supposed to be at the last frame of every chapter.
I was thinking about storing the data using cmi.suspend_data, is this
a wise choice?

import flash.external.ExternalInterface;
String(ExternalInterface.call("LMSGetValue", "cmi.suspend_data"));
// Is this the only thing I have to 'invoke' to get the data from the
suspend_data? //

String(ExternalInterface.call("LMSSetValue, ... ?
// I still don't know how to do this part, it's a action script thing,
I have to use. But in short, I'd like to take the string, and just
update the chapter that is completed and then send it back to
suspend_data //

String(ExternalInterface.call("LMSCommit", ""));


if (the string looks like = "1,1,1,1,1,1,1,1,1,1") { //
meaning: all chapers are completed //

String(ExternalInterface.call("LMSSetValue",
"cmi.core.lesson_status", "completed"));
String(ExternalInterface.call("LMSCommit",
""));
}
else // lets say: "1,0,0,0,1,1,1,1,1,1" or something //
{
String(ExternalInterface.call("LMSSetValue",
"cmi.core.lesson_status", "incomplete"));

rsug...@pivotpointelearning.com

unread,
Mar 31, 2011, 8:17:46 AM3/31/11
to elearning-technolo...@googlegroups.com

Hello David,

 

I’m not a Flash developer, but I’ve done this exact thing using JavaScript as follows:

 

1.  I use an array to capture chapter completion status

2.  Every five minutes and on course exit I send data to the LMS using LMSSetValue/LMSCommit

3.  When my function to send data to the LMS is called I loop through the chapter completion array and set a variable to true or false depending on whether all chapters are complete:
function sendDataToLms(){

          for (i = 0; i < aChapComp.length; i++){

                if(aChapComp[i] == 1)

                     var bAllComp = true;

                else{

                     var bAllComp = false;

                     break;

                }

          }

 

          if(bAllComp)

                LMSSetValue("cmi.core.lesson_status","completed");

          else{

                LMSSetValue("cmi.core.lesson_status","incomplete");

                LMSSetValue(“cmi.core.exit”,”suspend”);

                var sChapComp = aChapComp.join(";");

                LMSSetValue("cmi.suspend_data",sChapComp);

          }

          LMSCommit();

}

 

Raymond Sugel Sr
cid:image001.png@01CA9746.34805150
eLearning Consultant
224-293-4135 (O)

847-370-6163 (C)
rsug...@pivotpointelearning.com
www.pivotpointelearning.com

--

image001.png
Reply all
Reply to author
Forward
0 new messages