Nine Awesome Reads About the Experience API, and One Cartoon

| Comments

At Saltbox we really enjoy reading about Experience API (blog posts, journal articles, tweets, use cases, etc). We especially love it when people grasp how the standard can potentially impact their corner of the learning world as practitioners, developers or observers. So we decided to compile a list of our favorites! We hope you enjoy this curated list from some amazing people, especially if you’re just getting your feet wet with the Experience API (just released in v1.0! fist pump):

  1. Steve Flowers - “Tech, People And Systems” - Read this if you want to see 4 kick ass hypothetical use cases from the U.S. Coast Guard. The catchy stuff: Human performance, career progression, LMS unshackling, proficiency.

  2. Aaron Silvers - “The Turn” - A unique perspective from one of the most influential figures behind the Experience API (AKA “The Beard”). The catchy stuff: Seat at the table, breadcrumbs, data ownership, magic. “We’re going to make it happen.”

  3. Alex Mackman - “Thoughts On Tin Can And The Future Of eLearning” - One of the most clear explanations about why the Experience API is a solution to “the problem.” The catchy stuff: I got nothing, just an all around solid article.

  4. Koreen Olbrish - “Data Is Meaningless Out Of Context” - Read this to learn about real-life potential Experience API design/planning/privacy challenges. The catchy stuff: Performance metrics, pushy sales people, trough of disillusionment, no system is perfect, the comments (great discussion).

  5. Julie Dirksen - “Three Reasons Instructional Designers Need To Know About Tin Can” - A unique post that will really get you thinking about how to put the user at the center. The catchy stuff: Let’s build a playground instead of a box. WCIDWT (What Can I Do With That).

  6. David Kelly & Kevin Thorn - “Should Instructional Designers Care About The Tin Can API?” - The title says it all, but really, why should you care about an API when you don’t have coding skills as a designer? The catchy stuff: The hybrid ID/Dev. Instructional, strategic, & technical design gaps.

  7. Audrey Watters - “Learning Experiences: The Tin Can API” - This post outlines some underlying philosophical considerations about the nature of learning and how we describe it. The catchy stuff: A programmatic language for learning, the importance of educational APIs.

  8. Megan Bowe - “Four Ways Tin Can Will Change The Landscape Of Learning Technology” - Really good description of the 4 layers of the “Tin Can Onion” originally posted by Rustici Software. The catchy stuff: Modernization of SCORM, LMS liberation, real world performance.

  9. Russell Duhon - “Making Statements: Tin Can API Best Practices”. Ok a shameless plug here, but Russell’s technical prowess and programming elegance is unmatched! He is the lead maker of Wax LRS at Saltbox and describes best practices in this blog post. The catchy stuff: Russell is the 3rd leading contributor on the Experience API specification on GitHub and an information visualization expert. Drop him a line, he’s up to talk Tin Can anytime.

Cartoon: Float Mobile Learning - “A Closer Look: The Tin Can API In The Real World” - An extremely unique and fun way to explore what the Experience API looks like in the real world! The catchy stuff: Bob at Megacorp.

Did we miss any great Experience API reads? Please post them in the comments below!

A Big Day for the Future of Learning

| Comments

April 26th marked the official launch of the new Experience API 1.0 (xAPI) learning technology standard and big step forward in the evolution of learning & development.

The buzz continues to grow about the formal, informal, social, and mobile learning opportunities the xAPI affords. We are excited about the advanced data analysis we can provide learning professionals in order to improve training, and ultimately employee performance.

One of the most important things about the xAPI is that it changes the learning conversation from formal training and tracking of traditional metrics (e.g. completions, test scores, number of classes) to answering questions like “which questions least correlate to success (and can be eliminated)?” or “which learning path is most effective for new hire training?”.

Insights derived from data like that is what gets learning professionals a seat at the executive table. Our customers continue to push towards new frontiers in analyzing and contributing to learning experiences in the workflow. We’re going to support them by rolling out 1.0 support for Wax LRS in the near future, while continuing to provide best-in-class analysis, scalability, and validation.

A big thank you to all those who have spent time crafting this new standard and a specific call out to the ADL for leading the initiative. Everyone should be proud of the work they have done and the impact this will have on changing an industry.

Got some interesting ways you would like to use the xAPI? We’d love to talk about it, please contact us.

John Delano, Ali Shahrazad, and Russell Duhon Saltbox, Makers of Wax LRS

Data at the Point of Experience

| Comments

One of the biggest promises of the Experience API is delivering contextual information at the moment of need, such as the distribution of the answers so far after completing a question. Unfortunately, such aggregate data are expensive to compute in the client; it might take several seconds to retrieve the necessary information for even a few thousand answers.

One option is for systems providing contextual information to expose additional APIs for accessing the aggregated data. That may be reasonable for some scenarios, but for answer distribution and many others the approach poses overriding interoperability and portability issues.

Instead, I recommend that LRSs and reporting systems providing summary information intended for use at the point of experience materialize those summaries periodically into summary statements with a sketched form such as ‘LRS summarized the answers for Question 7 as ________’.

Using this Materialized Summary approach, clients can easily retrieve the summary statements to contextualize activity. What’s more, they’ll be able to use the stream of summary statements to provide greater contextual information than a naive API would, such as showing an instructor how answer densities evolve over time – perhaps one answer was popular early on, but right now another’s ahead.

The one part requiring notable implementation on the client is that many summaries will be delivered as extensions (almost always result extensions), but the burden for those on clients is much smaller than for a new API, and the same extensions could easily be used by multiple summarizers.

This is early days yet, what with 1.0.0 only just now coming upon us, but there’s already a burgeoning ecosystem of tools for working with the Experience API. The Materialized Summary pattern will help make the fruits of analysis reusable to enhance and extend experiences at the moment of need.

What data would help you contextualize experiences? Comment here or drop me an email if you have ideas, thoughts, or want to try some experiments.

How Tin Can Can Help: Save Out Experience Data

| Comments

This is the second in a series of posts (and somewhat belated, we’re pretty busy here at Saltbox) answering designer questions about how the Experience API, Tin Can, can help in different scenarios. For more introduction, take a look at the first post.

In this post I’m tackling a question posed by Julie Dirksen. Due to the nature of the question and answer, this post will be less about data analysis and more about Tin Can technology.

I’d like a learner to be able to save the data out of a learning experience that they think is relevant – to a virtual notebook, or a file they can print or whatever. How can Tin Can help do that?

A couple aspects of Tin Can are especially important here. One, Tin Can centers around statements, which use a document data model. Instead of needing to come up with a way to represent as data the use of a programming interface, or turn a series of low-level commands into a higher-level summary, Tin Can data is already in a usable form at a useful level of granularity.

Two, Tin Can has fairly strong semantics. Instead of arbitrary properties where meaning needs to be completely imposed from without, a well-crafted Tin Can statement is highly interpretable by itself, and can be made even more interpretable given a small amount of additional knowledge about the more flexible identifiers (such as verb and activity id).

Because of these aspects, groups of Tin Can statements can be understood remarkably well on their own, and can be moved about quite freely.

So, returning to the question, there are several ways we could answer it, and I’m going to touch on three options. One, people could capture data generated as they engage in learning activities at the point of engagement. Two, a system sitting in front of a Learning Record Store (LRS) could present and curate personal learning portfolios to people with data in the system. Or three, people could extract portable personal learning-related data from a central LRS (this might also be tied in with option two).

Starting with the first, easiest option, capturing data at the point of engagement. A learning activity could easily request personal LRS credentials from the learner right now. I suspect that in the future LRS connection will become even slicker, requiring people to do very little to have learning activity data they generate recorded. Not all learning activities will enable such behavior, though I hope it will be widely adopted.

Moving on to the second option, personal portfolios, we’re talking about a layer above an organization’s central LRS. This treats the LRS like it is commonly used today: as a specialized database (NoSQL & document-oriented if you’re curious) for learning-related data. The portfolio application would be a summarizing layer, showing important and relevant data, while hiding or de-emphasizing detailed data, such as the many choices made in a lengthy simulation.

Specific pieces of data could be included, excluded, commented upon, or otherwise manipulated using the built-in stucturing capabilities of the standard such as Statement References and Context Activities.

In the third option, an LRS could package up a selection of statements related to a given person along with human-readable descriptions and a cryptographic signature for the package contents into a single archive. These could be statements derived from a portfolio application, like the above, or they could be chosen by a simpler system.

The cryptographic signature makes it possible for another recipient (such as a new company the person was hired at) to verify the origin of the statements. Including the human-readable descriptions make them accessible in systems that don’t understand Tin Can. Using separate packaging instead of passing statements between LRSs avoids many of the potential coordination problems and encourages human review (since the receiving LRS might not be interested in all the data).

I’m hoping to see a lot of innovation in those three (and more!) directions for personal learning-related data. If you’re interested in talking more, I can be reached in the comments or at russell.duhon@saltbox.com. What do you want to enable people to do with the learning-related data they generate?

How Tin Can Can Help: Vehicle Fleet

| Comments

There’s been some great discussion around a post by Koreen Olbrish. One part of that discussion has been questions from designers around the idea of “How can Tin Can help?” Kevin Thorn and Julie Dirksen both posted a variety of scenarios, and I’m hoping we see a lot more.

I’m going to join the discussion and put on a second hat that doesn’t come up too often. My main hat nowadays is CTO of Saltbox – developer, ops, chicken sacrifices, and any other technical stuff, and that’s the hat I’m usually wearing when I participate in the Tin Can API standards discussion. This second hat is something that used to be a big part of what I did: social science researcher doing data analysis.

Because that’s what we’re mostly talking about when we ask how Tin Can can help in various situations: data analysis. Not always, but Tin Can itself doesn’t do much until the data it collects becomes a target for analysis. That means that a lot of the time what I’m talking about will be useful independently of Tin Can.

So, without further ado, the first in a series of “Tin Can Can” posts, the Vehicle Fleet scenario from Kevin Thorn.

I have a fleet of 4,000 vehicles. Brakes are replaced once a year. However, over 300 are being replaced 2-3 times a year. I need to know if those vehicles are in fact faulty, or do I have a driving performance problem with drivers riding their foot on the brake pedal? Can TinCan help me with that?

I’m going to make a simplifying assumption that vehicles have one primary driver in a time period. Most of what I talk about could still be done without that assumption, but the verbiage would be a lot more complicated.

Before we even talk about Tin Can, here’s what I’d do first. I’d make a stacked histogram of miles driven (presumably the company tracks this for the fleet) for vehicles having and not having brake failures in a given year. I’d also graph home locations (also a reasonable assumption for tracking) on a map, coloring vehicles having brake failures to stand out.

That might be enough. If I see that most brake failures seem reasonably explained by miles driven and/or geography, then there would be relatively few people who might be assisted by training or performance support. If I see the relationship, I could check my simple model by looking for drivers who have multiple brake replacements go away or appear with a change in driving frequency.

Assuming I don’t see that, or I have some reason for seeking out more detail, I would next look into patterns for particular drivers. I would probably make another histogram, showing how many drivers had more than one brake failure in a year how many times. If there aren’t very many with multiple years, that suggests the problem has little to do with the driver, or if it does, drivers quickly adjust. To check for the latter, I might make a dot plot showing times a car had multiple breakdowns vs years the driver had been driving for the company at that time.

If I see the latter, or brake problems don’t correlate with miles driven/location but do correlate with driver*, I’m going to start becoming confident that there’s an opportunity for training or performance support.

Now I’m going to talk about Tin Can.

My sketch above leads to two scenarios. In one, we have reason to believe that drivers start out more likely to need brake replacements, but acquire skills that avoid the problem over time. In the other, we have not explained why drivers need brake replacements, but it seems to be caused by something related to particular drivers – quite likely something about how those drivers are driving, since miles driven and geographic location, two likely culprits, have been looked into. I’ll call the first the “performance ramp” scenario and the second the “repeated problem” scenario.

The performance ramp scenario is probably the Tin Can-friendliest, because the evidence suggests specific behaviors exist and can be acquired that address the problem, and moreso that there are existing experiences that lead to that acquisition. Conveniently, what we have on hand is an Experience API.

I see several possible approaches. In one approach, we would turn the detailed experience of any given drive into stylized happenings recorded via Tin Can. That could be through an automated system (send a statement when braking hard, send a statement when RPM exceeds a certain amount at rest, et cetera) or through ridealong observers with checklists (I predict there will be a number of Tin Can enabled checklist/survey offerings). Then I would look at summaries of that stylized data to see different patterns – for instance, I might apply Principal Components Analysis and color the points by whether or not the driver had repeated brake problems.

If that approach is not feasible, I would consider a training course for new drivers specifically tailored to reduce brake abuse, then watch the brake replacements of (randomly assigned) course graduates closely vs others in their cohort. One thing to be careful of would be enrolling sufficient drivers to get useful results. If the course reduced the cost of brake replacements by more than the cost of running the course, I’d expand it to all new drivers. I’d Tin Can stylized accounts of student behavior similarly to see how they interacted with test scores (recorded by Tin Can).

The repeated problem scenario is trickier. Approaches like described above may help, or may not. For instance, if the problem is sufficiently heterogenous, fixing it may be more expensive than the problem itself. If I implemented collection of stylized data, I would consider using matching to find possible causes. To match a problem driver’s record, find the most similar record among non-problem drivers. By matching a few problem drivers and looking at the characteristics in the differences with their matches individually, patterns may be visible that suggest causes.

And that’s how I’d investigate the problem described, and where I would bring in Tin Can to do so. I’m going to reuse techniques from this post again and again: stylized performance data, matching, histograms, checklists, and so forth. What makes Tin Can so important is that many applications of these techniques will be able to build with reusable components that may be composed in other situations to solve other problems.


*I’ve only talked about fairly simple graphs for getting at this information, but in reality there are much more powerful toolkits to do so. They are also, however, more complicated, and for many problems people care about often unnecessary. Even experienced practitioners will often see things in simpler graphics that they miss in more complicated models. If you’re interested in learning more, I’m talking about regression. In a situation like this, if I felt the need, I’d probably start with a simple regression using locale and miles driven, then if that didn’t work do a multilevel regression including those and locale. If I had a large number of variables, I’d consider a regression tree.

A Web of String: PKI Signatures and the Tin Can API

| Comments

DISCLAIMER: None of the new URIs in this post should be considered final.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
{
  "actor": {
    "mbox": "mailto:russell.duhon@saltbox.com",
    "name": "Russell Duhon"
  },
  "verb": {
    "id": "http://adlnet.gov/expapi/verbs/created",
    "display": {
      "en": "created"
    }
  },
  "object": {
    "id": "http://blog.saltbox.com/blog/2012/12/31/a-web-of-string-pki-signatures-and-the-tin-can-api/",
    "definition": {
      "name": {
        "en": "A Web of String: PKI Signatures and the Tin Can API"
      }
    }
  },
  "context": {
    "contextActivities": {
      "parent": {
        "id": "http://blog.saltbox.com/"
      }
    },
    "extensions": {
      "http://saltbox.com/extensions/wot#custody": {
        "created": {
          "signature": "-----BEGIN PGP SIGNATURE-----
          Version: GnuPG v1.4.11 (GNU/Linux)

          iQEcBAABAgAGBQJQ4eyyAAoJEE7mDEdQvcuSQhIIAL0f56yyFDa/G3rpzDBeJbYy
          kgJPDrKx9RneAObaUVwvi6sVB/n59W/wbR9BtSZWj5bRwX+qehYWlHcI5ShbR+MY
          LwzCE0V4FyStgah7X3+qYWLyi2NNKdBilVBmtFFXzTyCxK7n0l5iPQNNC692TGMT
          KEieFhJXp+nhMr9gFifDSKqqrDKpXCcLfHfU4QLKdn4iJqtAZSsCZ8zoXyokurTN
          4WECm+dp0NlwqOWlUPvaqKWHL62olHU7w6eDjIPRW49CWRqKnI4irdmiGb6DoB/7
          U3FofqcpH/CDW8TUq6EWXwoVKmyIuSyhNHsNDKoWYp1R5kCeidMGl8uSqdy2g4k=
          =aAY8
          -----END PGP SIGNATURE-----",
          "accepted": {
            "signature": "-----BEGIN PGP SIGNATURE-----
            Version: GnuPG v1.4.11 (GNU/Linux)

            iQEcBAABAgAGBQJQ4fDrAAoJEE7mDEdQvcuSCw4H/1+aBfgYvo4kis9BXy3t529E
            /ufu8Y++ZnqwMHVCmUTLMheVndDC5zkp+GhLWfZ/8pg69IxJIG4gqIVhKfC8l5gW
            VluGsO+4lqiyvyQ3m94ozbjZLRduG7je9Gb6ZzY53QCBc0mOcUlcvkNAN+XyChga
            wyenJsgZkSLFUH63y4CpSi5f27Vb9b0qkGovgvL0dC1kbGwrzKRmJzxV7VpSdwDK
            r62vuyoqRk66mC9WeXbi5TRfTPhBdHK/9nnZxTWFxxKQkt2mL04HtinFL5eN6jec
            XC/mX6tkDUs2WuY0SxbFTRzOv237KAHsUDmHgvWmPi58aL8oVpWl1J+aRQm6kus=
            =YkPf
            -----END PGP SIGNATURE-----",
            "lrs": "http://lrs.example.com/XAPI/",
            "trust": "marginal"
          }
        }
      }
    }
  }
}

I’ve been thinking about options for using public key infrastructure (PKI) and web of trust (WOT) concepts in the Tin Can API/Experience API to support a number of important scenarios. Spurred by a conversation with @ellenm1 I’ve put together this blog post.

There are numerous possible applications of PKI in the API, such as encrypted payloads, but I think the biggest likely applications relate to cryptographic signing. PKI is asymmetric: the person or system that needs to sign things has a secret key that they use to do it, and anyone with the public key can verify that the content has not been changed and was signed by exactly that secret key. This can be very useful, if they know about the secret key and trust the owner.

I’ve come up with three use cases for verifiable cryptographic signatures with Tin Can:

  • verifying the original sender and form of a statement
  • given a trusted exchange between the original sender and the receiving LRS, a chain of custody appears and can be maintained even as the statement is passed between loosely coupled LRSs under certain constraints
  • additionally, if someone wants to certify the content of a statement, they can issue an additional statement to do so

Verifying the Original Sender

When a system creates a statement, it can sign a normalized form of the statement, and place the signature in the context extensions before sending, something like below:

1
2
3
4
5
6
7
8
9
10
11
12
13
"created": {
  "signature": "-----BEGIN PGP SIGNATURE-----
  Version: GnuPG v1.4.11 (GNU/Linux)

  iQEcBAABAgAGBQJQ4eyyAAoJEE7mDEdQvcuSQhIIAL0f56yyFDa/G3rpzDBeJbYy
  kgJPDrKx9RneAObaUVwvi6sVB/n59W/wbR9BtSZWj5bRwX+qehYWlHcI5ShbR+MY
  LwzCE0V4FyStgah7X3+qYWLyi2NNKdBilVBmtFFXzTyCxK7n0l5iPQNNC692TGMT
  KEieFhJXp+nhMr9gFifDSKqqrDKpXCcLfHfU4QLKdn4iJqtAZSsCZ8zoXyokurTN
  4WECm+dp0NlwqOWlUPvaqKWHL62olHU7w6eDjIPRW49CWRqKnI4irdmiGb6DoB/7
  U3FofqcpH/CDW8TUq6EWXwoVKmyIuSyhNHsNDKoWYp1R5kCeidMGl8uSqdy2g4k=
  =aAY8
  -----END PGP SIGNATURE-----"
}

This approach requires no cooperation on the part of the receiving LRS, since once the statement arrives at the LRS it is kept immutable, so a retriever can be sure that, if it gets a signed statement from an LRS, it received it in the signed form (to the extent it trusts the LRS at all). A badly behaved system might sign things as created that it didn’t create, but that behavior would be quickly noticed and the signing identity treated as untrusted.

In fact, a signed statement such as this is only useful to the extent the receiving system trusts the signer, and by default there’s no trust. With just a signed statement, establishing trust needs to happen out of band, perhaps by standard Web of Trust mechanisms or just direct assignment. An LRS could provide an administrator with a list of unknown signers for the administrator to investigate. There’s another option, though, the…

Chain of Custody

An LRS can initiate a chain of custody when it receives a signed statement. Layers of the chain of custody are nested, so that when normalizing to check a given layer, you cut off the lower levels and verify all the levels above as well as the rest of the statement content.

The beginning of a chain of custody looks something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
"created": {
  "signature": "-----BEGIN PGP SIGNATURE-----
  Version: GnuPG v1.4.11 (GNU/Linux)

  iQEcBAABAgAGBQJQ4eyyAAoJEE7mDEdQvcuSQhIIAL0f56yyFDa/G3rpzDBeJbYy
  kgJPDrKx9RneAObaUVwvi6sVB/n59W/wbR9BtSZWj5bRwX+qehYWlHcI5ShbR+MY
  LwzCE0V4FyStgah7X3+qYWLyi2NNKdBilVBmtFFXzTyCxK7n0l5iPQNNC692TGMT
  KEieFhJXp+nhMr9gFifDSKqqrDKpXCcLfHfU4QLKdn4iJqtAZSsCZ8zoXyokurTN
  4WECm+dp0NlwqOWlUPvaqKWHL62olHU7w6eDjIPRW49CWRqKnI4irdmiGb6DoB/7
  U3FofqcpH/CDW8TUq6EWXwoVKmyIuSyhNHsNDKoWYp1R5kCeidMGl8uSqdy2g4k=
  =aAY8
  -----END PGP SIGNATURE-----",
  "accepted": {
    "signature": "-----BEGIN PGP SIGNATURE-----
    Version: GnuPG v1.4.11 (GNU/Linux)

    iQEcBAABAgAGBQJQ4fDrAAoJEE7mDEdQvcuSCw4H/1+aBfgYvo4kis9BXy3t529E
    /ufu8Y++ZnqwMHVCmUTLMheVndDC5zkp+GhLWfZ/8pg69IxJIG4gqIVhKfC8l5gW
    VluGsO+4lqiyvyQ3m94ozbjZLRduG7je9Gb6ZzY53QCBc0mOcUlcvkNAN+XyChga
    wyenJsgZkSLFUH63y4CpSi5f27Vb9b0qkGovgvL0dC1kbGwrzKRmJzxV7VpSdwDK
    r62vuyoqRk66mC9WeXbi5TRfTPhBdHK/9nnZxTWFxxKQkt2mL04HtinFL5eN6jec
    XC/mX6tkDUs2WuY0SxbFTRzOv237KAHsUDmHgvWmPi58aL8oVpWl1J+aRQm6kus=
    =YkPf
    -----END PGP SIGNATURE-----",
    "lrs": "http://lrs.example.com/XAPI/",
    "trust": "marginal"
  }
}

All the signers of a chain of custody (distinct from the creator’s signature) should be LRSs. As a statement is passed from LRS to LRS (that some of understand chains of custody), the chain is maintained, so that the content remains the same as when it truly originated can be reasonably trusted under some conditions, even if the verifying system is not aware of the creator’s identity.

When an LRS fetches a statement from another and finds a chain of custody signature, if the LRS has at least minimal trust (out of band) of the signer of any link of the chain of custody, it may extend the chain at the last trusted link. Yes, this does mean modifying the statement, but it’s a modification in an extension in the vein of authority changing as the statement changes hands, and so acceptable.

This means that a client receiving a statement can, if it trusts any link in the chain, be reasonably sure that the statement originated in the form given. However, that’s not a very big guarantee – a verifiably issued statement is not necessarily good and accurate, and it would be useful to have a verifiable, portable way to say that about statements, a statement to…

Certify the Content of a Statement

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
{
  "actor": {
    "mbox": "mailto:russell.duhon@saltbox.com",
    "name": "Russell Duhon"
  },
  "verb": {
    "id": "http://saltbox.com/verbs/wot#certifies",
    "display": {
      "en": "certifies"
    }
  },
  "object": {
    "id": "55555555-5555-5555-5555-555555555555",
    "objectType": "StatementRef",
    "http://saltbox.com/extensions/wot#signature":
        "-----BEGIN PGP SIGNATURE-----
        Version: GnuPG v1.4.11 (GNU/Linux)

        iQEcBAABAgAGBQJQ4eyyAAoJEE7mDEdQvcuSQhIIAL0f56yyFDa/G3rpzDBeJbYy
        kgJPDrKx9RneAObaUVwvi6sVB/n59W/wbR9BtSZWj5bRwX+qehYWlHcI5ShbR+MY
        LwzCE0V4FyStgah7X3+qYWLyi2NNKdBilVBmtFFXzTyCxK7n0l5iPQNNC692TGMT
        KEieFhJXp+nhMr9gFifDSKqqrDKpXCcLfHfU4QLKdn4iJqtAZSsCZ8zoXyokurTN
        4WECm+dp0NlwqOWlUPvaqKWHL62olHU7w6eDjIPRW49CWRqKnI4irdmiGb6DoB/7
        U3FofqcpH/CDW8TUq6EWXwoVKmyIuSyhNHsNDKoWYp1R5kCeidMGl8uSqdy2g4k=
        =aAY8
        -----END PGP SIGNATURE-----"
  },
  "result": {
    "response": "I checked this statement out with the instructor",
    "extensions": {
      "http://saltbox.com/extensions/wot#signature":
        "-----BEGIN PGP SIGNATURE-----
        Version: GnuPG v1.4.11 (GNU/Linux)

        kgJPDrKx9RneAObaUVwvi6sVB/n59W/wbR9BtSZWj5bRwX+qehYWlHcI5ShbR+MY
        iQEcBAABAgAGBQJQ4eyyAAoJEE7mDEdQvcuSQhIIAL0f56yyFDa/G3rpzDBeJbYy
        LwzCE0V4FyStgah7X3+qYWLyi2NNKdBilVBmtFFXzTyCxK7n0l5iPQNNC692TGMT
        KEieFhJXp+nhMr9gFifDSKqqrDKpXCcLfHfU4QLKdn4iJqtAZSsCZ8zoXyokurTN
        4WECm+dp0NlwqOWlUPvaqKWHL62olHU7w6eDjIPRW49CWRqKnI4irdmiGb6DoB/7
        U3FofqcpH/CDW8TUq6EWXwoVKmyIuSyhNHsNDKoWYp1R5kCeidMGl8uSqdy2g4k=
        =aAY8
        -----END PGP SIGNATURE-----"
    }
  }
}

It’s possible to talk about other statements using statements. In this case, a statement has been made about another statement, using StatementRef along with a signature to verify the contents of the target statement. Then, this statement is signed, so that its contents cannot be messed with. Any client receiving this statement, who trusts the signer, has strong reason to believe the certified statement’s contents. This sort of verifiability will be important for exchanging especially important and sensitive data.

Other Uses

PKI is an extremely useful technology, and I’m sure signing will see other uses with statements, such as simple trusted source bulk statement exchange across untrusted intermediaries. If you’ve got ideas, I’d love to hear them in the comments.

General Notes

A key part of all this is statement normalization. It must be possible for all parties, seeing just the statement, to agree on what the normalized form of the statement is. In rough outline, normalizing a statement should include: deleting the stored time and authority, serializing the JSON in a particular way, requiring the ID and timestamp to already be set, requiring a particular format of the timestamp, and so forth. It is not simple, but since it will be a very exactly specified profile, it will not be hard to meet.

Another consideration is that private keys MUST be kept private for all this to work. Anyone who exposes, however briefly, a private key has made that private key completely useless. This means signing must always take place on the server side.

Making Statements: Tin Can API Best Practices

| Comments

As more and more tools create Tin Can API/Experience API statements, best practices are starting to come together. In particular, I’ve seen certain approaches prove themselves effective while working with customers of our Wax Learning Record Store. I’ve compiled several of those best practices here, for reference.

Client-side statement ID

All statements sent to an LRS have an ID. If no ID is provided in the submitted statement, an ID is automatically generated for the statement. This ID is a UUID, so even systems that do not coordinate can reliably assign unique identifiers. Early Tin Can adopters are taking advantage of the flexibility of the standard, and often sending statements to multiple Learning Record Stores. Those Learning Record Stores might later communicate back and forth. If the ID of the statement is generated by the LRS, then each statement will have different ones. Instead, clients should generate statement IDs, so that all LRSs agree on the ID of the same statement. While this is a best practice, it is only a best practice if your tool generates a real UUID, using one of the standard algorithms. The best way to do that is by using a UUID library (for instance, one is included in the Python standard library). If you do not have a UUID library available, please send each statement only to one LRS first and allow it to generate the UUID (it will then return the generated UUID for you to use in the future).

Use context->registration to group statements

A statement’s context has an optional registration attribute (also a UUID, like statement IDs). Systems generating statements should use registration to group related statements logically. For instance, a quiz sending statements about individual questions and the overall quiz result should probably give the statements from one attempt a single registration. This will help systems later separate out which statements go with which attempt.

Be specific with verbs

Overly general verbs, such as “experienced” or “interacted” are not very useful. Use specific verbs that closely denote the desired action, occurence, or state of being. When other systems query, then, they will be able to focus in very specifically on the happenings of interest. Overly general verbs, though, will often require the system sort through far more data, and possibly require awkward heuristics in an attempt to distinguish the real thing that happened. If a system is interested in a more general set of verbs, it will only be necessary to compile a list of the more specific verbs that apply.

Share and reuse verbs

The vision for a future full of shared data will happen. It will be a lot smoother if when designing statements and a specific and appropriate verb is already in use somewhere, people reuse that verb. The only way people will discover verbs already in use is if those using verbs share them. Saltbox maintains a verb repository at https://github.com/fugu13/tin-can-verbs/ . Any verbs in use are welcome (see the link for contribution directions).

Provide an activity type in activity definitions

Activities, in addition to an ID, can have a type, which must also be a URI. Using activity types provides similar benefits to using specific verbs, creating another easy dimension for slicing and dicing. With statements using specific verbs and activity types, it will be easy to narrow in on people who watched a play, as distinct from people who watched a movie or people who performed in a play.

Use anchors when coining reusable URIs

Activity types, verb IDs, and extensions keys are three areas where URIs will be frequently reused. A handy best practice, borrowed from various Semantic Web technologies, is to combine a base URI for a related set of URIs and then make specific URIs with anchors on that base URI. For instance, http://example.com/xapi/verbs/sports#medaled and http://example.com/xapi/verbs/sports#slalomed. Then hosting descriptions of those verbs is a simple matter of putting up a single page at the base URI (note: while it is nice if the anchors resolve to parts of the page, there is no requirement that they do, and it is not a big deal if they do not).

Use Agent Verb Agent statements

In Tin Can, the object of a statement can be another Agent. “Person A gave a gold star to Person B”. “Person A scanned the badge of Person B”. Statements in this form are verb powerful, and should be used where appropriate. If there is an Activity involved, put it in the context.

In Agent Verb Agent statements, the actor should be the agent doing the learning

If it isn’t clear who is doing the learning, the Agent taking the more active role should be the actor.

Fill in as much of result and context as makes sense

What this means will change from situation to situation, but often times there are attributes in result and context that map naturally to data involved in the triggering event. Sometimes this is obvious, such as putting success true or false for events that have a concept of positive or negative outcomes, and sometimes it involves a less intuitive mapping, such as putting an amount of money involved as a raw score (though that may be more obvious when the money is part of keeping score in a game).

Use contextActivities

Even more specifically than using result and context when possible, take advantage of contextActivities, in context. The grouping, parent, and other activities are a valuable chance to sketch a piece of hierarchy, association, or similar connection between what’s happening in the statement and other pieces of information. While this can be awkward (such as how you can only have one of each, at least in this version of the standard), it helps turn statements from isolated happenings into linked data that can be effectively browsed, connected, and analyzed.

What are some statement-related best practices you’ve run into?

Tin Can API: Go From Zero to Production in 2 Days

| Comments

We are pleased to announce that we are offering the first Tin Can API technical workshop! This workshop is designed to take any developer or instructional technologist from having a basic understanding of the Tin Can API to enabling an existing or new application to generate Tin Can statements!

Thank you Mojocat Creative Services & Surf Incubator for producing and powering this exciting workhop! We hope to see you there.

Learn more about this workshop!

Matching/Filtering for Tin Can Statements

| Comments

Tin Can API statements flexibly describe learning-related events. Like many event-focused systems, use cases frequently involve ‘watching’, ‘filtering’, and generally reacting to events having certain characteristics. While there are many extremely capable systems and libraries for performing those operations, there’s still a need for a portable, flexible way of defining filters that match or don’t match individual statements, for use as part of those larger systems.

After hearing from several people about scenarios where I saw such filters providing part of the solution, I started brainstorming. The results are below.

Generally speaking, a statement is of interest if a particular part of the statement has a value meeting certain criteria, or if combination of parts-meeting-criteria using and, or, or not apply. Other important considerations for the solution are ease of implementation, readability, and concision.

The solution I’ve come up with looks like this:

1
2
3
4
5
6
7
8
9
{
    "or": {
        "$.result.success": true,
        "$.result.score.scaled": {
            ">": 0.5
        }
    },
    "$.verb.id": "http://adlnet.gov/expapi/verbs/completed"
}

That filter would match any statement with the ‘completed’ verb and either a successful result or a scaled score over 0.5.

The keys that start with a $ are JSONPath expressions. JSONPath is a standard, similar to XPath, for selectively extracting data from JSON documents.

The result of the JSONPath expression is compared to the value. If the value is not an object, that part of the filter matches if and only if the result of the JSONPath expression exactly matches the value. If the value is an object, the key(s) in that object define how the value must match, such as “>” above (in addition to the obvious mathematical operators, options will probably include intersection, set membership, subset, nonexistence, and so forth).

Inside value objects and in the filter as a whole, multiple filters are “anded” together, and combined/derived filters may be created by nesting filters inside “and”, “or”, and “not”.

Note: to ensure portability and security, no ‘script expressions’ are allowed in JSONPath in a filter.

If you like what you’re reading, comment or otherwise get in touch. I hope to see this work eventually lead to a community standard that can be applied in many scenarios and across many platforms where statements flow.

Google Course Builder and the Tin Can API

| Comments

Google’s Course Builder is a great resource for the MOOC community. Open source, simple, and easily modified, a programming novice can go from download to a working MOOC ready for thousands of people in a short period of time. Rapidly authored and easily deployed MOOCs are fantastic, but create a new problem: data integration. Many people will participate in multiple MOOCs over time, and bringing that data together will deliver a lot of value for individuals and organizations.

So, I’ve added simple support for the Tin Can API (also known as the Experience API) to Google Course Builder. The Tin Can API is a modern standard for communicating learning data, intended for recording learning anywhere and everywhere it occurs. In a typical Tin Can ecosystem, learning applications and related systems, such as a Google Course Builder MOOC, produce ‘statements’ describing the learning ‘activities’ that ‘agents’ are engaging in, such as completing assessments, answering questions, and completing the course as a whole (I added support for all three of those). Those statements are then sent to a ‘Learning Record Store’ (LRS), where they can be sliced, diced, and generally considered.