Contact Us Support Forum Get Email Updates
 
 

Thanks! Someone will be in touch with you shortly.

Rather just email us? Email us here.
Rather speak with someone in person?
Call any time with Experience API questions:

866.497.2676

Deep Dive: Actor/Agent

Posted by

Categories: APIs, Best Practices, Deep Dive, Statement Anatomy, Statements, xAPI

Posted 5 June 2013

 

Does a statement get recorded in an LRS if there is no one there to experience it?

 
The Problem with Abstractions

The Experience API is designed for recording information about experiences, but one of the assumptions is that someone, or a group of someones, has to be the experiencer. Enter the term “actor”, often referred to as the “I” in a xAPI statement, or grammatically, the subject.

There is a lot of abstraction in building xAPI statements, and at first glance, defining the “I” of a statement seems simple and concrete enough that we should start there. Unfortunately, it just isn’t that simple, “Who am I?” is a pretty big question of the ages and that question didn’t get any smaller in xAPI. To compound the issue, not only do you have to define the “I”, or perhaps the “royal we”, you have to tell someone else that it was you. And to further complicate matters, maybe you aren’t interested in just being “I,” but you want to bring along your friend “me” (or friends, “us”). And don’t get me started on “myself”. Okay, enough pronoun soup for a while, back to actor…

To understand the ‘actor’ portion of a statement, it is helpful to take a step back and understand the distinction between a key or property (left hand side of an assignment) versus the value (right hand side of an assignment) in a JSON object. Ultimately a statement is made up of properties that have values assigned to them, ‘actor’ is one such property, and in the case of ‘actor’ its value must be an Agent (or Group). This means that ‘actor’ is simply a placeholder (or pointer) and doesn’t have a concrete, standalone representation.

In other words we don’t think of an “Actor” as a noun itself, or type of object, we think of “actor” as pointing to a specific value. Also note that I’m using “actor” (lowercase) versus “Agent” uppercase to distinguish between properties of a statement and the types of values they hold. This is the point where my wife tells me that I am just playing semantics, and if she were a developer I would retort that is *precisely* what I’m doing because semantics are very important to me (us). (By the way, she isn’t a developer, so I don’t retort at all or I’d be experiencing the doghouse.)

Defining Agents and Groups

Agents then, are a type of object, and all we can really know for sure about that agent is that its representation is consistent because all we have to represent an agent is an inverse functional identifier, which is a fancy way of saying a unique ID that we can trace back to the same entity. That inverse functional identifier can take several forms for Agents, e-mail address (or mbox) being the most easily understood. Along with the raw human readable e-mail address an Agent can be identified by the SHA1 hash of their email address (well, it has to be an IRI so it includes the “mailto:” part).

{
    mbox: "mailto:info@tincanapi.com",
    objectType: "Agent"
}

 

{
    mbox_sha1sum: "f427d80dc332a166bf5f160ec15f009ce7e68c4c",
    objectType: "Agent"
}

 
Moving beyond e-mail, an agent may be uniquely identified by their OpenID URI. While e-mail is still a pretty universally accepted concept and OpenID has taken off in some circles, the specification also provides for a more system specific variation such that an agent can be identified by combining a unique identifier for a given system, say Twitter.com, and their unique representation on that system, for instance their “Twitter handle”; the combination is known simply as an “account.” While some systems will come and go, and we may eventually see the end of e-mail addresses, the concept of a unique ID for a system plus that system’s unique ID for an entity (account as a concept) should be flexible enough to last as long as the spec will.

{
    account: {
        homePage: "http://twitter.com",
        name: "projecttincan"
    },
    objectType: "Agent"
}

 
One important note, while an Agent may have multiple inverse functional identifiers available for use an Agent object should only include one of them in a given representation to avoid learning record stores from rejecting such requests for privacy reasons related to linking of inverse functional identifiers. And, oh yeah, an Agent can have a ‘name’ so that us humans can more easily associate with it, too.

{
    mbox: "mailto:info@tincanapi.com",
    objectType: "Agent",
    name: "Info at TinCanAPI.com"
}

 
In the above examples we also explicitly included the ‘objectType’ property set to “Agent,” that property can be left out whenever an object must be either an Agent or a Group and defaults to an Agent (such as in the ‘actor’ property).

Groups are similar to Agents in that they are a type of object used to represent an entity, but with the potential of an additional property that allows a group to enumerate all or some of its constituents, specifically the ‘member’ property. Groups must provide the ‘objectType’ property with a value of “Group.” Groups come in two flavors: identified and unidentified (or anonymous). In the former case an identified group has an inverse functional identifier (or unique ID) just as an Agent does, and may or may not include its ‘member’ property. If an identified group includes a ‘member’ property with a list, it should not be assumed to be an exhaustive list, meaning that a statement may call out a specific subset of member Agents for an experience (perhaps the famous ones, or the biggest donors, or the best dressed, or the first to arrive). In the latter case an anonymous group is not associated with any uniquely identifying information, therefore does not have an inverse functional identifier, but must include the “member” property. Although the specification, as of 1.0.0, leaves it open that the member list in this case need not be exhaustive, it is a best practice to make it so, as there is no way to associate other Agents with that part of the statement. And although it is a natural inclination to associate unidentified groups with the exact same set of member Agents as the same group, the specification draws attention to the fact that implementing systems should not make this assumption. Additionally, both kinds of groups’ member lists must only include Agents, therefore it is not possible to nest Groups.

{
    mbox: "mailto:info@tincanapi.com",
    name: "Info at TinCanAPI.com",
    objectType: "Group",
    member: [
        {
            mbox_sha1sum: "48010dcee68e9f9f4af7ff57569550e8b506a88d"
        },
        {
            mbox_sha1sum: "ca3ffdb44c4727137e29ebf42ee80c2afdd8d328"
        },
        .
        .
        .
    ]
}

 

{
    objectType: "Group",
    member: [
        {
            mbox_sha1sum: "90f96ca8c3ae315f0e40df4e16772eb6d05e3937"
        },
        {
            mbox_sha1sum: "ca3ffdb44c4727137e29ebf42ee80c2afdd8d328"
        }
    ]
}

 
Back to Statements

Now with our Agent/Group objects in hand we just drop them into the “actor” property and away we go; however there are other places in a statement where an Agent can be used as well. The “me” instance mentioned earlier can be accomplished by placing my Agent or Group (“us”) in the ‘object’ property to form a statement similar to “Sam (Agent 1) helped me (Agent 2)”. In that case, the statement uses two Agents, the “actor” property still contains one, Sam in this case, along with the one used in “object,” Brian (or me) in this case. Agents or Groups can also be included in the ‘context’ of a statement as an ‘instructor,’ leading to statements of the form “Brian (actor) learned xAPI from Ben (instructor).” Context can also include a ‘team’ property but it must be a Group.

Last but not least, an Agent is used to populate the ‘authority’ property of a statement, but generally statement creation is done with out this, leaving it to be populated by the LRS (more on “authority” in a future post).

Outside of Statements

Since Agents are set into some of the most valuable parts of a statement’s makeup, they need to be query-able. Agent objects are passed via the “agent” query parameter to the statements API for retrieving statements that have a matching ‘actor’ or ‘object’ property. Send a request with the “related_agents” query flag turned on to find statements where an Agent exists in one of the other possible locations as well.

Agents are cool enough that they get their own API methods, known as the Agent Profile. Agent Profiles really warrant their own post for the future, but for now it is enough to say that we can associate arbitrary data with a particular Agent in an LRS using them. One example use case is storing user preferences. Along with the Agent Profile, Agents are also composed into the State API calls.

Gotchas

Besides being part of innumerable groups in most cases, these days a given person probably has many inverse functional identifiers as well. I personally have three e-mail accounts that I consider separate, one personal, one business, and one for various other things. And each one of those technically has aliases in about ten other domain names. Each of those could be considered distinct inverse functional identifiers, so that means I have about thirty ways to be identified, just by e-mail, not to mention I have at least ten public facing profiles (such as Twitter, Github, Facebook, LinkedIn, Google+, etc.) which all have an “account” concept that could be used in Experience API communications, and those are just the public ones. The key takeaway here is that systems working with Experience API need to account for the fact that a “Person” may have any number of unique identifiers.

Additionally, in all of the inverse functional identifier cases, we can’t know whether that e-mail address or account is a shared one or not, so while an Agent can be loosely associated with a person it should not be assumed to represent a single person. For that matter, we probably shouldn’t assume that it is even a human on the other end. There is also the issue of time and the fact that e-mail addresses or accounts can change hands, for example “info@tincanapi.com” could be sent to any number of people or “brian@example.com” might change hands from Brian Miller to Brian Smith. Ultimately that is just one of the reasons a ‘timestamp’ property exists, but we’ll get to that in a later post.

Go now, make statements!

 
  • Pingback: Deep Dive: Activity - Experience API()

  • Sreekanth

    I read in the spec sheet that we can specify an array containing aliases of an agent identified by an unique ID. In that case, if I am using 5 different email IDs to access various learning contents, but if those 5 email IDs are defined in the array that is tagged to my unique ID, the LRS will not have any ambiguity in storing all my learning activities against my name. Is my understanding correct?

    Also, is there a provision to add 6th mail ID or say, Library account number to the existing array? Is it a tedious job?

  • bsc_scorm

    Sounds like you’re talking about the “agent profile API” (https://github.com/adlnet/xAPI-Spec/blob/master/xAPI.md#agentprofapi) and the ability to “Return a special, Person Object for a specified Agent.”

    This is a service the LRS provides when it has a way of associating several IDs belonging to the same person. The way the LRS gets this information is outside the scope of the spec. So the process to add this information depends on the specific LRS implementation, and indeed the LRS is not required to provide a way to do so. I haven’t seen much done with this yet, but expect it to be used for reporting, particularly where an LRS is integrated into a larger system that has such information.

    Back in version 0.90, it was possible to specify such a list of IDs directly in an agent object, but this capability was removed for privacy reasons.

  • Pingback: Deep Dive: 'object' - Experience API()

  • Pingback: Deep Dive: Extras/Others - Experience API()

  • Pingback: Webinar Q&A Follow Up: Anatomy of a xAPI Statement - Experience API()

  • I’m thinking about indentified groups’ Inverse Functional Identifiers. In your example you use mbox as identifier. I would like to identify groups using ‘account’ object to refer to the group from my LMS, but I’m wondering how to separate accounts of Agents from accounts of Groups if they both have numerical identifier (so there could be a person with id: 10 and a group with id: 10). In xAPI reference account shoud be in form

    “account”: { “homePage”: “http://www.example.com”, “name”: “1625378” }

    but here I have to seperate somehow names of groups or homepage URLs from the person’s names. The best way to identify person or group is it’s Id.

    Have you faced with problems like this? Could you suggest the best way to solve it?

  • Brian Miller

    Good question. I think the property name “homePage” is a little misleading as it doesn’t have to be an actual “home page”, it doesn’t even have to be resolvable. It is really just another identifier. So in your case I’d create two separate home pages, one for identifying Agents and the other for Groups. Normal URI coining rules apply, so you should use a FQDN and hopefully it is one you own or have permission to use. If you want to make the resulting identifiers actually resolve, and possibly redirect (or otherwise handle) to a login page that’d be even better. So you’d end up with:

    { “homePage”: “http://yourdomain.com/tc/agent”, “name”: “10” } and
    { “homePage”: “http://yourdomain.com/tc/group”, “name”: “10” }

    Which are distinct Accounts and therefore distinct IFIs.

    HTH,

    Brian

  • Thank you, Brian!
    I thought about using homePage property to separate agents from groups, but was confused by it’s name (home page) and a relation with FOAF spec. Now it’s clear that homePages doesn’t have to be home/login pages.

  • Constantine

    Entities like verb, actor, activity have unique identifier(Inverse functional Identifier / IRL) and some additional information like metadata(or name for actor). Should LRS update this metadata(or name) when receive verb/activity/actor with changed values ? And if we should, how it’s recomment to do it: merge or override?

  • Constantine

    I think it would be better to illustrate my question. Suppose we send following statements to LRS consistently. Can you help me to understand what will be changed in persisted data in LRS after every statement sending and what should be returned to client on calling GET to /statements in that time?

    Statement #1
    {
    “actor”: {
    “name”: “Sally Glider”,
    “mbox”: “mailto:sally@example.com”
    },
    “verb”: {
    “id”: “http://adlnet.gov/expapi/verbs/completed”,
    “display”: {“en-US”: “completed”}
    },
    “object”: {
    “id”: “http://example.com/activities/hang-gliding-test”,
    “definition”: {
    “type”: “assessment”,
    “name”: { “en-US”: “Hang Gliding Test” },
    “description”: { “en-US”: “The Solo Hang Gliding test, consisting of a timed flight from the peak of Mount Magazine” },
    “extensions”: {
    “http://example.com/gliderClubId”: “test-435”
    }
    }
    },
    “authority”: {
    “name”: “Irene Instructor”,
    “mbox”: “mailto:irene@example.com”
    }
    }

    Statement #2
    {
    “actor”: {
    “name”: “Sally”,
    “mbox”: “mailto:sally@example.com”
    },
    “verb”: {
    “id”: “http://adlnet.gov/expapi/verbs/completed”,
    “display”: {“en-US”: “successfully completed”, “en”: “completed!”}
    },
    “object”: {
    “id”: “http://example.com/activities/hang-gliding-test”,
    “definition”: {
    “type”: “assessment”,
    “name”: { “en-US”: “The Test” },
    “description”: { “en-US”: “The Solo Hang Gliding test, consisting of a timed flight from the peak of Mount Magazine” },
    “extensions”: {
    “http://example.com/gliderClubId”: “test-435”
    }
    }
    },
    “authority”: {
    “name”: “Irene Instructor”,
    “mbox”: “mailto:irene@example.com”
    }
    }

    As i understand, all localization data must be updated by merge, so what version of it we will receive from LMS with Statement #1 now? Or it must not be changed at all ?

    Statement #3
    {
    “actor”: {
    “name”: “Sally Changed By Actor”,
    “mbox”: “mailto:sally@example.com”
    },
    “verb”: {
    “id”: “http://adlnet.gov/expapi/verbs/completed”,
    “display”: {“en-US”: “successfully completed”, “en”: “completed!”}
    },
    “object”: {
    “id”: “http://example.com/activities/another-test”
    },
    “authority”: {
    “name”: “Sally Changed By Authority”,
    “mbox”: “mailto:sally@example.com”
    }
    }

    After Statement #3 situation comes much more interesting. Look that we use the same Inverse Functional Identifier in authority and actor, but different names. What should we get now on trying sending GET request for this three statements ?

  • bsc_scorm

    It varies by the type of identifier, and the LRS has a lot of leeway here. For activities, the canonical definition is relevant when querying statements with format=canonical. For Agent’s, the agent profile API. For verbs the LRS isn’t actually expected to maintain canonical display information, but the specification talks about canonical verb display information for the benefit of reporting systems.

    I don’t think there is a clear best practice yet for the decision to merge vs completely overwrite definitions, or when to do so. Ideally it shouldn’t matter much as any differences in metadata for the same identifiers should be trivial except in cases of malice or recklessness. To guard against malice, the LRS can restrict metadata updates to users with privileges proportional to the importance of the identifiers for which metadata is being updated. Eg: the description of a high stakes test could be initially entered by a highly privileged user or loaded from it’s URI, and then the LRS could reject updates from less privileged users. In the case of recklessness, it’s extremely unlikely that a poorly chosen ID will collide with a well thought out ID, and the problem of poorly chosen IDs colliding with each other will work itself out as people using such IDs find they have no useful data.

  • bsc_scorm

    I wrote the reply above before I saw your follow up, but to address it specifically, statements are always returned as sent except for activity definitions. So with regard to reading statements back, only the change to activity definitions are relevant.

    The concept of a canonical verb display or agent name is more important for reporting systems that display aggregates.

  • How is “Person” , “Agent “, Agent Profile” related. One Agent can have many profile ids. A Person is a collection of Agent Profiles ? How these three are compared in a common base ? In real life, a person is referred to “Person” / “Agent”/ “Agent Profile” in LRS ?

  • Rafi

    Is a group an ordered list or an un-ordered list or members? Should the order be stored in the LRS? The spec is unclear. It calls it an array, which implies that it is ordered. But then everywhere else it seems like it is used as an un-ordered list.

  • Brian Miller

    I would set “Profile” aside for the moment. The titling in the spec isn’t terribly clear on this, but it is easiest to think about “profiles” as just document stores similar to “state”. In other words a “profile” corresponds to the “/agents/profile” resource.

    When talking about “Person” objects you are referring to the other resource in that subsection of the specification, specifically the “/agents” resource as that is the only place the Person object is used. Note that it is a read-only resource, and it is LRS implementation specific on how that LRS gets the information on how to merge Agents into a Person object.

    So, then back to your questions:
    An Agent is a unique way to identify a physical entity, in other words a single inverse functional identifier (IFI) (read: id), such as an email address or system account. A physical entity may have more than one Agent representation.

    A Person is not a collection of Agent profiles, it is instead a combined representation of the IFIs for all Agents known to the LRS to represent the same physical (or “real world”) entity. So a Person object representing me as a physical entity includes my work email address, personal email address, twitter account, facebook account, etc. all combined into a single object.

    An Agent Profile then is the list of all profile IDs (and corresponding documents) associated with a specific Agent which has a single IFI.

    HTH,

    Brian

    (You may be interested in this previous discussion of these terms: https://groups.google.com/a/adlnet.gov/d/topic/xapi-spec/vLcZ3nS3czg/discussion )

  • Brian Miller

    After some internal discussion we concluded that based on the description in the specification for a Group including words like “cluster” and “constituent” that the LRS is not required to maintain order. Because of this a client shouldn’t expect the members of a Group to be returned necessarily in the same order. Having said that, you are correct that there is room for interpretation around this point, and arrays are necessarily ordered, and I can even think of a use case where you’d want to maintain order so it would be helpful for the specification to disambiguate this point. To that end we’ve raised a GitHub issue for more discussion, see that here:

    https://github.com/adlnet/xAPI-Spec/issues/537

    HTH,

    Brian

  • Brian,

    After reading your Gotchas section above it does explain some things about mbox in particular. I started using the account Object IFI (https://github.com/adlnet/xAPI-Spec/blob/master/xAPI.md#4123-inverse-functional-identifier) instead of mbox since our system currently allows the user to modify their email address but not their account username. I would like to also send email address if possible to the LRS but I ran across this rule for using Agent objects (e.g. “An Agent MUST NOT include more than one (1) Inverse Functional Identifier;” – https://github.com/adlnet/xAPI-Spec/blob/master/xAPI.md#4121-when-the-actor-objecttype-is-agent). How can I also send the email information if I need to utilize it later?

    “actor”: {
    “account”: {
    “homePage”: “http://eduworkforce-dev-ztraboo”,
    “name”: “ZacharyTrabookis”
    },
    “name”: “Zachary Trabookis”,
    “objectType”: “Agent”
    },

  • Richard Klancer

    Brian,

    I am curious if there is any agreed-upon way to make statements about anonymous users. The methods above seem to imply it’s necessary to know the “actual” identity of the learner (at least to the point where we can identify them with an email address or a user account on some well-known system)

    Use case:

    At http://concord.org/ we make physics simulations, data analysis tools, and similar things for K12 students, wrap them in learning activities we make available on the web, and then do research on how they affect learning.

    We would like to do analytics on granular actions taken by learners in the environments we provide — similarly to https://seanputman.wordpress.com/2013/08/05/tracking-user-clicks-using-the-xapi-part-2/ — and we are considering xAPI and Learning Locker for this purpose.

    Often we make our tools available without login — in fact lots of extra bureaucratic requirements kick in once a user does log in — which means we need a sensible way to describe”anonymous user 359″.

    We could invent something ad hoc but I’m wondering if there’s a community-supported way to do this (imagine the day when i can see that anonymous user 1757 was sent to us from Khan Academy and was anonymous user 4859661 there — consider that ad exchanges do this kind of handoff now)

  • Brian Miller

    In so far as pretty much anyone is identifiable on the Internet in some way, not really.

    But I think you can achieve your goal with the ‘account’. I don’t believe it is as strong as you are thinking when you said “some well-known system”, it only has to be a “well known” system to the person interested in the metrics, and the account on that system doesn’t need to be tied to any personally identifiable information until you need it to. Nor does it have to be an actual “login”. So you could create an agent that has a ‘homePage’ of “http://concord.org/” and a ‘name’ that is just a UUID or some other non-identifying but unique string and use that (anonymous359 would work). The only difficulty is dealing with multiple sessions and whether the actor needs to know their identifier for returning to the system later, but that can be as simple as having them write it down.

    There isn’t any reason why your system can’t pair the Agents as you describe. In other words, {“homePage”: “http://khanacademy.org/anonymous”, “name”: “user4859661”} can be linked to {“homePage”: “http://concord.org/”, “name”: “anonymous1757”} by your system. The user need only know (write down) their identifier for your system is “1757” (or their KA identifier “4859661”) so that when they return their data is linked.

    HTH,

    Brian

  • Brian Miller

    In general you shouldn’t need to, you would be better off keeping a map of the known e-mail addresses for a given Agent on the system instead. That works for keeping the ‘mbox’ in the Agent as well (if you’d prefer). So while you have a primary email address linked to the account for account purposes, you’d keep a list of email addresses associated with that account and ideally the time periods for when that person (or entity) had that address.

    Having said that, if you *must* for some reason have the email address in the statement then you could always use an ‘extension’, probably in ‘context’. If you went that route I’d probably make it a full Agent object and use the ‘mbox’ representation to make it easier to parse and exchange.

    HTH,

    Brian

    Sorry for the delay I didn’t get notified about this comment.

  • Richard Klancer

    Thanks, Brian.

    I should add that my question was mostly about normative expectations; does “everyone” do anonymous-user reporting this way? (No need to reply, unless you happen to have knowledge of a discussion of this topic elsewhere!)

    What triggered my question, I now realize, was this language in the spec: “Learning experiences become meaningless if they cannot be attributed to identifiable individuals and/or groups” — see “Rationale” under
    https://github.com/adlnet/xAPI-Spec/blob/89b97c6bc22c8489a6bbee0c7d2edc5e278d48ad/xAPI.md#4123-inverse-functional-identifier

    This seemed to be discounting the utility of xAPI for detailed analytics of individual’s activities in simulations, games, or complex tools, where insights gained from analytics needn’t be attributed back to the individuals who get “credit”; in turn this made me wonder if xAPI was *too* rooted in the LMS and “enterprise e-learning” fields for our purposes.

    However, there’s also a reading of that phrase which amounts to “subject-verb-object triples need to have a subject, and a given person or group does lots of things that result in lots of triples, you probably want those triples to have the same subject”

  • Brian Miller

    I don’t really have evidence of other discussion. I can tell you that whenever someone has posited this type of question I’ve given the same advice, so in so far as I carry some clout and have discussed this with Andrew Downes and Ben Clark internally, I suppose it could be considered a best practice?

    After this discussion Andrew has opened an issue against the spec repo for this, see:

    https://github.com/adlnet/xAPI-Spec/issues/617

    I think you’ve captured both the original intent of the wording and the specification, and the realization over time that the community has come to, that the specification is far more powerful than just capturing e-learning data. So while I think the rationale holds for e-learning based data, I do think the spec community at large knows that it may have larger implications for data capture beyond originally envisioned, and the ‘account’ property in particular can be leveraged for anonymized data capture. So yes, “triples are great, and a triple necessarily has a subject” fits well for the way I think of it.

  • Sam

    Hi Brian, I have a question regarding your Gotchas section. Assuming the LRS supports the linking of multiple identifiers into a single Person object: how should an LRS handle a GET request using different agent identifiers that refer to the same person?

    Is there any reason that a request to get statements using one person’s mbox identifier shouldn’t return statements relating to that same person that were recorded with a facebook account identifier?

    Or if AP performs a GET request for statements, should the LRS return just statements that were recorded with that same agent identifier?

    Thanks in advance.

  • Brian Miller

    This is a good question, but the specification is pretty explicit that it is the single inverse functional identifier, the one found in the statement, that must match.

    “Filter, only return Statements for which the specified Agent or group is the Actor or Object of the Statement. * Agents or identified groups are equal when the same Inverse Functional Identifier is used in each Object compared and those Inverse Functional Identifiers have equal values.”

    Based on my read of that the LRS can’t provide statements that are related via the Person concept in the normal statements stream. Having said that, it would be entirely reasonable for an LRS add on with extended query capability to provide this functionality.

  • AwokeKnowing

    Hi Brian,
    scormcloud sends an Actor in the launch querystring parameters. But if you send it right back as is, you get an “error: expected string, got array…”. I find it unfathomable that if the LMS says that’s the “actor” and you send that back as the “actor” it tells you it’s wrong.

    so scormcloud sends:
    ?actor={
    “name”:[“Billy G”],
    “account”:[{
    “accountServiceHomePage”:”http://cloud.scorm.com/”,
    “accountName”:”AAAABBBBEX|billyg@microsoft.com”
    }],
    “objectType”:”Agent”
    }
    nevertheless, from what I can see of the tetris example, it parses the array of names, and then sends back
    “actor”: {
    “name”: “Billy G”,
    “account”:{
    “homePage”: “twitter.com”,
    “name”: “AAAABBBBEX|billyg@microsoft.com”
    },
    “objectType”:”Agent”
    }

    is this some kind of hidden “standard”? how universal is it? shouldn’t returning the actor as supplied by the LMS at launch work? And how is this “flexible” if it can’t handle the array?

  • AwokeKnowing

    please disregard, I’m reading the spec. I just cannot believe xAPI complicated something so basic as the LMS providing and identifier and the activity using THAT identifier to reference the student. I guess we have different design philosophies.

  • Brian Miller

    So this is where history is really important…

    Back in the days of xAPI (when it was xAPI) there was a release of the specification at 0.9 which had Agents (that serve as actors) that looked like the example you give as what Cloud provides to a xAPI package (something that was imported with a `tincan.xml` file) which is based on a set of guidelines released at that time that were peripheral to the specification itself. What I refer to now as the xAPI Launch Guidelines, which is what the major rapid authoring tools implemented against, and can be found here:

    https://github.com/RusticiSoftware/launch/blob/master/lms_lrs.md

    It was then determined for various technical reasons that the “IFI merging” that was a “feature” of the array based Agents was going to be a disaster and subsequently removed in the 0.95 specification and going forward. However, the launch guidelines as an implemented side piece that was never intended as a long term specification was not ever updated to match and even had they been the implementation itself needed to stay the same lest all the existing content at the time would have broken. So, because Cloud had already implemented xAPI at 0.9 (and still supports it) and had implemented the launch guidelines and people were using it already it is locked into what was needed to support xAPI packages at the time which is why that query parameter has the form that it has. So, the actor as provided by Cloud can be sent directly to the LRS on Cloud and have it work, it just has to be done using the 0.9 version of the protocol. (If you can find another LRS that supports 0.9 then it should work there too, that’ll probably be difficult.)

    Technically xAPI doesn’t even include the concept of launch, so this is really for a separate specification to determine, and the best option going forward (though there is very little adoption at the moment) is to use cmi5 which has similar mechanics but provides an actor value that can be used in a 1.0.x LRS directly. See:

    https://experienceapi.com/launch/
    https://experienceapi.com/cmi-5-tin-can-api/

    So the design philosophy isn’t really relevant, you have to understand the need for backwards compatibility and therefore the history to know why it is the way it is.

  • AwokeKnowing

    Thanks Brian, I can appreciate backwards compatibility. So can you confirm that as long as you put the version number in the http header that corresponds to the LMS, sending back the actor as provided by the LMS should work. I know you don’t control every LMS, but is that the idea?

    Also, how do you figure out the version?

  • Another reply…

  • Brian Miller

    I’m not quite sure I followed your first question. And you may have meant LRS in the first case?

    The version number likely doesn’t matter with the LMS, as far as I know every LMS that will have implemented the launch guidelines will be providing the actor as a .9 Agent. In that case you’d always have to convert it when communicating with a 1.0.x LRS. The problem then becomes that the version of the LRS is not provided to the content. There are three choices for determining the version,

    1) Know ahead of time what version the LRS the content will be talking to supports and hard code it,
    2) Guess and assume at this point that every LRS can talk a 1.0.x version (a pretty reasonable thing to do, cause any LRS supporting 0.9 and 0.95 likely supports 1.0.x)
    3) Ask the LRS via the /about resource which is the intended reason for this to exist, though I think it isn’t being used much in the wild,

    One of the primary reasons TinCanJS exists (and most of the other libraries) is to solve this problem. Your code *may* work, though based on that in TinCanJS it looks like it leaves out parts, I’m curious if there is a reason to not use TinCanJS? Using try/catch for validation is often seen as a code smell, granted that is somewhat subjective. There are better ways to check whether a value is an Array in JS these days. Also there are *a lot* of other problems, particularly with browser environments, that TinCanJS solves when communicating with the LRS that you should keep in mind before choosing to write your own handling.

    HTH,

    Brian

  • Arturo

    Hi!
    Kind a dummy question, sorry.

    The encrypted email in SHA1 is only the email address? (example@test.com) Or with the “mailto:” part? (mailto:example@test.com)

  • Brian Miller

    The spec says:

    > The hex-encoded SHA1 hash of a mailto IRI (i.e. the value of an mbox property).

    The key there is the use of “IRI” which requires a “scheme” portion be included, so for a “mailto IRI” you would include “mailto:” as the scheme. So the example you gave would be correct. This also allows a system to compute the SHA1 for the full value of the `mbox` property in a similar Agent object and then match the two without having to parse that scheme off.

    Important note: the spec also says:

    > The domain portions of email addresses are case insensitive. Clients SHOULD use lowercase for the domain portion of the email address when calculating the SHA1 hash for the “mbox_sha1sum” property.

    HTH,

    Brian