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?

Comments