The Caliper Analytics® Specification provides a structured approach to describing, collecting and exchanging learning activity data at scale. Caliper also defines an application programming interface (the Sensor API™) for marshalling and transmitting event data from instrumented applications to target endpoints for storage, analysis and use.
caliper-js is a reference implementation of the Sensor API™ written in Javascript.
- master: stable, deployable branch that stores the official release history.
- develop: unstable development branch. Current work that targets a future release is merged to this branch.
caliper-js releases are tagged and versioned MAJOR.MINOR.PATCH[-label] (e.g., 1.1.3).
Pre-release
tags are identified with an extensions label (e.g., "1.2.0-RC01"). The tags are stored in this repository.
We welcome the posting of issues by non IMS Global Learning Consortium members (e.g., feature requests, bug reports, questions, etc.) but we do not accept contributions in the form of pull requests from non-members. See CONTRIBUTING.md for more information.
- Read the Caliper Analytics® Specification.
- Fork the IMS Global caliper-js project to your Github account and clone your copy to a local development machine.
- Install Node.js and the Javascript package manager, npm. Consider using nvm, a node version manager, to install Node.js and npm.
- Once npm is installed, change directories to where you cloned caliper-js and install the browserify and grunt-cli packages globally before installing the caliper-js dependencies:
npm install -g browserify
npm install -g grunt-cli
npm install
Clone the IMS Global caliper-common-fixtures repo at the same level as caliper-js. Then invoke Grunt to execute the unit tests, generate JSDocs, and build the caliper-js library.
grunt
The distribution file will be copied to dist/caliperSensor-[MAJOR.MINOR.PATCH].js.
The Caliper Analytics® Specification
defines a set of concepts, relationships and rules for describing learning activities. Each activity
domain modeled is described in a profile. Each profile is composed of one or more Event types
(e.g., AssessmentEvent, NavigationEvent). Each Event type is associated with a set of actions
undertaken by learners, instructors, and others. Various Entity types representing people, groups,
and resources are provided in order to better describe both the relationships established between
participating entities and the contextual elements relevant to the interaction (e.g., Assessment,
Attempt, CourseSection, Person).
caliper-js implements all profiles described in Caliper Analytics® Specification.
A Caliper Event describes the relationship between two entities, one an actor and the other an
object, formed as a result of a purposeful action undertaken by the actor at a particular moment
in time and, optionally, situated within a given learning context. Below is an example of an
AssessmentEvent expressed as JSON-LD:
{
"@context": "http://purl.imsglobal.org/ctx/caliper/v1p1",
"id": "urn:uuid:27734504-068d-4596-861c-2315be33a2a2",
"type": "AssessmentEvent",
"actor": {
"id": "https://example.edu/users/554433",
"type": "Person"
},
"action": "Started",
"object": {
"id": "https://example.edu/terms/201801/courses/7/sections/1/assess/1",
"type": "Assessment",
"dateToStartOn": "2018-11-14T05:00:00.000Z",
"dateToSubmit": "2018-11-18T11:59:59.000Z",
"maxAttempts": 1,
"maxScore": 25.0
},
"generated": {
"id": "https://example.edu/terms/201801/courses/7/sections/1/assess/1/users/554433/attempts/1",
"type": "Attempt",
"assignee": "https://example.edu/users/554433",
"assignable": "https://example.edu/terms/201801/courses/7/sections/1/assess/1",
"count": 1,
"dateCreated": "2018-11-15T10:15:00.000Z",
"startedAtTime": "2018-11-15T10:15:00.000Z"
},
"eventTime": "2018-11-15T10:15:00.000Z",
"edApp": "https://example.edu",
"group": {
"id": "https://example.edu/terms/201801/courses/7/sections/1",
"type": "CourseSection",
"courseNumber": "CPS 435-01",
"academicSession": "Fall 2018"
},
"membership": {
"id": "https://example.edu/terms/201801/courses/7/sections/1/rosters/1",
"type": "Membership",
"member": "https://example.edu/users/554433",
"organization": "https://example.edu/terms/201801/courses/7/sections/1",
"roles": ["Learner"],
"status": "Active",
"dateCreated": "2018-08-01T06:00:00.000Z"
},
"session": {
"id": "https://example.edu/sessions/1f6442a482de72ea6ad134943812bff564a76259",
"type": "Session",
"user": "https://example.edu/users/554433",
"startedAtTime": "2018-11-15T10:00:00.000Z",
"extensions": {
"request": {
"id": "d71016dc-ed2f-46f9-ac2c-b93f15f38fdc",
"hostname": "example.edu",
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36"
}
}
}
}Note the following requirements illustrated by the above example:
- Caliper events and entity describes are serialized as JSON-LD documents. Each document must be
provisioned with a JSON-LD
@contextthat references, at a minimum, the remote IMS Caliper context document. If you are unfamiliar with JSON-LD, consider pausing here and augmenting your Caliper knowledge by reading the JSON-LD specification. - The
Eventpropertiesid,type,actor,action,objectandeventTimeare required; all other properties are considered optional. Theidvalue must be expressed as a UUID using the formurn:uuid:<UUID>per RFC 4122. A version 4 UUID should be generated. Thetypevalue must match the term specified by Caliper (e.g., "AssessmentEvent", "MessageEvent", "NavigationEvent"). - Caliper permits
Entityvalues to be expressed either as a JSON object or as a string corresponding to the resource's IRI. If theEntityis expressed as an object, both theidandtypeproperties must be specified; all other properties are considered optional. Theidvalue must be expressed as an IRI. A URI using the URN scheme may be provided although care should be taken when employing a location-independent identifier since it precludes the possibility of utilizing it to retrieve machine-readable data over HTTP. Thetypevalue must match the term specified by Caliper (e.g., "Person", "Assessment", "Attempt", "CourseSection"). - Date/Time properties (e.g.,
eventTime,dateCreated,startedAtTime) must be expressed as date and time values expressed with millisecond precision using the ISO 8601 format YYYY-MM-DDTHH:mm:ss.SSSZ set to UTC with no offset specified. - Custom attributes not described by Caliper may be included but must be added to the
extensionsproperty as a map of key:value pairs. - Properties with a value of null or empty are excluded by caliper-js during serialization.
See the The Caliper Analytics® Specification for a complete description of requirements.
caliper-js provides two factory functions to simplify creating events and entities:
- eventFactory() returns a mutated
Eventobject based on a caliper-js delegate to which is assigned an options object of user-provided key:value pairs. The function exposes a single method signature: .create(delegate, opts). - entityFactory() returns a mutated
Entityobject based on a caliper-js delegate to which is assigned an options object of user-provided key:value pairs. The function exposes two method signatures: .create(delegate, opts) and .coerce(delegate, opts). Use the .create(delegate, opts) method to express a CaliperEntityas an object; use the .coerce(delegate, opt) method to express anEntityas a string that corresponds to its IRI.
var obj = entityFactory().create(Assessment, {
id: "https://example.edu/terms/201801/courses/7/sections/1/assess/1",
dateToStartOn: moment.utc("2018-08-16T05:00:00.000Z"),
dateToSubmit: moment.utc("2018-09-28T11:59:59.000Z"),
maxAttempts: 1,
maxScore: 25.0
});var edApp = entityFactory().coerce(SoftwareApplication, {id: "http://example.edu"});Caliper Event and Entity data must be transmitted inside a Caliper Envelope, a JSON data
structure that includes metadata about the emitting application sensor and the data payload.
The sensor, sendTime, dataVersion and data properties are required. The data array
comprises an ordered collection of one or more Caliper Event and/or Entity describe documents.
Each Event and Entity describe transmitted inside an Envelope must be serialized as a JSON-LD
document.
{
"sensor": "https://example.edu/sensors/1",
"sendTime": "2018-11-15T11:05:01.000Z",
"dataVersion": "http://purl.imsglobal.org/ctx/caliper/v1p1",
"data": [{
"@context": "http://purl.imsglobal.org/ctx/caliper/v1p1",
"id": "urn:uuid:7e10e4f3-a0d8-4430-95bd-783ffae4d916",
"type": "ToolUseEvent",
"actor": {
"id": "https://example.edu/users/554433",
"type": "Person"
},
"action": "Used",
"object": {
"id": "https://example.edu",
"type": "SoftwareApplication"
},
"eventTime": "2018-11-15T10:15:00.000Z",
"edApp": "https://example.edu",
"group": {
"id": "https://example.edu/terms/201801/courses/7/sections/1",
"type": "CourseSection",
"courseNumber": "CPS 435-01",
"academicSession": "Fall 2018"
},
"membership": {
"id": "https://example.edu/terms/201801/courses/7/sections/1/rosters/1",
"type": "Membership",
"member": "https://example.edu/users/554433",
"organization": "https://example.edu/terms/201801/courses/7/sections/1",
"roles": ["Learner"],
"status": "Active",
"dateCreated": "2018-08-01T06:00:00.000Z"
},
"session": {
"id": "https://example.edu/sessions/1f6442a482de72ea6ad134943812bff564a76259",
"type": "Session",
"startedAtTime": "2018-11-15T10:00:00.000Z"
}
}]
}Install and build the caliper-js library per the steps above. Then reference the caliper-js library inside the <head> tag of your HTML file:
<head>
<script src="[path to]/caliperSensor-1.1.1.js"></script>
</head>You can use caliper-js to create, serialize and transmit Caliper messages to a target endpoint over
HTTP. The HTTP connection must be secured and encrypted using Transport Layer Security (TLS). Below
is a snippet of code illustrating the steps required to create an AssessmentEvent and transmit it
to a target endpoint:
// Initialize Caliper sensor
var sensor = Caliper.Sensor;
sensor.initialize("http://example.org/sensors/1");
// Override default HTTP options
var options = Caliper.Clients.HttpOptions;
options.uri = "https://example.edu/caliper/target/endpoint";
options.headers["Authorization"] = "40dI6P62Q_qrWxpTk95z8w";
// Initialize and register client
var client = Caliper.Clients.HttpClient;
client.initialize("http://example.org/sensors/1/clients/2", options);
sensor.registerClient(client);
// Set Event property values
// Note: only actor, action, and object property assignments shown
var actor = entityFactory().create(Person, {id: "https://example.edu/users/554433"});
var action = actions.started.term;
var obj = entityFactory().create(Assessment, {
id: "https://example.edu/terms/201801/courses/7/sections/1/assess/1",
dateToStartOn: moment.utc("2018-08-16T05:00:00.000Z"),
dateToSubmit: moment.utc("2018-09-28T11:59:59.000Z"),
maxAttempts: 1,
maxScore: 25.0
// ... add additional optional property assignments
});
// ... Use the entityFactory() to mint additional entity values.
// Create Event
var event = EventFactory().create(AssessmentEvent, {
id: id,
actor: actor,
action: action,
object: obj,
eventTime: new Date().toISOString(),
edApp: edApp,
group: group,
membership: membership,
session: session
});
// ... Create additional events and/or entity describes.
// Create data payload
var payload = [];
payload.push(event);
// Envelope options
var opts = {
sensor: sensor.id,
sendTime: new Date().toISOString(),
dataVersion: "http://purl.imsglobal.org/ctx/caliper/v1p1",
data: payload
};
// Create envelope with data payload
var envelope = sensor.createEnvelope(opts);
// Delegate transmission responsibilities to client
sensor.sendToClient(client, envelope);This project is licensed under the terms of the GNU Lesser General Public License (LGPL), version 3. See the LICENSE file for details. For additional information on licensing options for IMS members, please see the NOTICE file.
©2018 IMS Global Learning Consortium, Inc. All Rights Reserved. Trademark Information - http://www.imsglobal.org/copyright.html