Tracking Registration Results
Technically, Engine does all the tracking for you, but Engine doesn't have any idea what you intend to do with the data it captures. As discussed in the overview, your system should store its own version of the registration state to display to the user in your application and for other reporting purposes. We provide a couple of ways of keeping that data in sync with the registration state in Engine.
Automatic Postbacks
Engine can be configured to send postbacks to an endpoint in your system (webhook) whenever it receives an update to the status of one of your registrations.
To configure this, set the value for ApiRollupRegistrationPostBackUrl
to the URL of the endpoint in your application you've built to listen for the postbacks. Once you've done that, Engine will POST a JSON object describing the status of a registration to your application whenever it receives an update from the player about an in-progress course launch.
These postbacks will be of the same schema that is used by the API. To make it easier to handle the postback coming from Engine, you can reference the API client's classes. You can translate the body of the postback into a RusticiSoftware.Core.api.client.v2.Model.RegistrationSchema
using your favorite JSON parser.
For example, the JSON response body from a registration postback would look much like this (based on the ApiRollupRegistrationFormat
setting discussed later):
{
"id": "my_test_reg_id",
"instance": 0,
"updated": "2020-09-29T19:19:03.107Z",
"registrationCompletion": "COMPLETED",
"registrationSuccess": "FAILED",
"score": {
"scaled": 40.0
},
"totalSecondsTracked": 12.0,
"firstAccessDate": "2020-09-29T19:19:00.550Z",
"lastAccessDate": "2020-09-29T19:19:03.107Z",
"completedDate": "2020-09-29T19:19:03.107Z",
"createdDate": "2020-09-29T19:18:45.247Z",
"course": {
"id": "my_test_course_id",
"title": "Golf Explained - Run-time Advanced Calls",
"version": 0
},
"learner": {
"id": "my_test_learner_id",
"firstName": "Waylon",
"lastName": "Smithers"
}
}
These are the namespaces you'd need to import to use the sample code.
using Newtonsoft.Json;
using RusticiSoftware.Core.api.client.v2.Model;
using System;
Then, provided that the string variable jsonString
contains the JSON from the postback, this code would deserialize it into the RegistrationSchema
class.
RegistrationSchema schema = JsonConvert.DeserializeObject<RegistrationSchema>(jsonString);
Console.WriteLine(schema.Id); //output = 'my_test_reg_id'
Console.WriteLine(schema.RegistrationCompletion); //output = 'COMPLETED'
Console.WriteLine(schema.Learner.FirstName); //output = 'Waylon'
These are the packages you'd need to import for the sample code.
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializer;
import RusticiSoftware.Core.api.client.v2.Models.RegistrationSchema;
Then, provided that the String variable jsonString
contains the JSON from the postback, this code would deserialize it into the RegistrationSchema
class.
Gson gSonInstance = new GsonBuilder()
.registerTypeAdapter(OffsetDateTime.class, (JsonDeserializer<OffsetDateTime>)
(json, type, context) -> OffsetDateTime.parse(json.getAsString()))
.create();
RegistrationSchema schema = gSonInstance.fromJson(jsonString, RegistrationSchema.class);
System.out.println(schema.getId()); //output = 'my_test_reg_id'
System.out.println(schema.getRegistrationCompletion()); //output = 'COMPLETED'
System.out.println(schema.getLearner().getFirstName()); //output = 'Waylon'
Note for legacy integration customers: If you want to switch to using Engine's automatic postback functionality, then you will need to make a few modifications to your integration. First, your integration layer should include an additional reference to RusticiSoftware.Engine.API.dll
include RusticiSoftware.Api-engine.jar
in the project's build path. Second, your "main" integration class (the one currently inheriting from DefaultIntegration
) should instead be changed to inherit from RusticiSoftware.Engine.api.implementation.BaseApiIntegration
. And lastly, our BaseApiIntegration
provides its own implementation of RollupRegistration
to handle the automatic postbacks, so you will need to remove your implementation of that method from your main integration class.
Postback Configuration
There are a few settings that allow you to configure the behavior of Engine's automatic postbacks. The only setting required to enable the automatic postbacks to your system is ApiRollupRegistrationPostBackUrl
, but you can set any of the following to further customize the behavior.
ApiRollupRegistrationFormat
: The level of detail in the information that is posted to you. It may be one of three values:course
(course summary),activity
(activity summary, the default value), orfull
(all detail available). If you are only reporting on top-level details (like completion, success, score, duration), then we recommend that you set this value tocourse
to minimize the amount data that has to be POSTed from Engine to your application.ApiRollupRegistrationAuthType
: Indicates how to authorize against the given postback URL. You can specify eitherform
orhttpbasic
, withhttpbasic
as the default value. Ifform
authentication, the username and password for authentication are submitted as form fieldsusername
andpassword
, and the registration data as the form fielddata
. Ifhttpbasic
authentication is used, the username and password are placed in the standard Authorization HTTP header, and the registration data is the body of the message.ApiRollupRegistrationAuthUser
: Username to use when posting registration data.ApiRollupRegistrationAuthPassword
: Password to use when posting registration data.
Postback Frequency
By default, Engine's SCORM player does not immediately save data committed by the content. Instead, it checks for dirty data on a set frequency (every 10 seconds), and if any is found, it sends it back to Engine to save in the database. Whenever Engine receives these updates, it will trigger the automatic postbacks to your system mentioned above.
Some customers have found this to happen a bit too frequently, increasing the load on Engine, and also on their own system to process the registration postbacks. To combat this, you can turn down the frequency at which the player checks for dirty data during the launch, as so reduce the frequency of possible postbacks to your endpoint.
To adjust this frequency, add an entry in your Engine config file for PlayerCommCommitFrequency
, and give it a value higher than the default of 10 seconds. The value should be in milliseconds, so you will want to change its value to something like 30000 or even 60000. You should be careful, though, not to set this value so high that the player posts back too infrequently. If anything happens that would unexpectedly cut the learner's connection to Engine, the learner will lose all of the progress they've accrued since the last time the player sent an update. You will want to find a sweet spot between minimizing server load and ensuring the safety of your learners' progress.
Note: For learning standards other than SCORM (AICC, xAPI, LTI), this postback frequency does not apply. Those other standards communicate progress directly to Engine at a frequency determined by the content itself.
Querying the API
At any time, you can retrieve the current state of a registration using the /registrations/{registrationId}
endpoint. This endpoint will return the same JSON schema that you receive in the registration postbacks. You might use this as a way to manually refresh a registration's state when needed.
Please remember that these resources are not intended to be ad hoc reporting mechanisms. Fetching all of the information about the registrations is not a small task, and repeatedly hitting these endpoints can result in negative effects on Engine's performance.
When calling the /registrations
endpoint, there are a few parameters that control how much detail about the registration is returned:
includeChildResults
: this includes details about each activity or learning object in the course, not just the top-level summaryincludeRuntime
: includes the runtime data of the activitiesincludeInteractionsAndObjectives
: this goes a step further and includes objective and runtime interaction details (questions/answers)
We disable these parameters by default, as each one adds additional processing time for creating the result. Many customers only care about the top-level details anyway, so this lets you include them only if actually needed.
API Results Limit and Paging
Although Engine and its REST API are not intended to be used for heavy reporting, some endpoints, like GET requests to /registrations
and /courses
, have the ability to return lengthy arrays of results. Consequently, Engine limits how many items Engine returns at once. Two settings, ApiResultLimit
and ApiResultLimitSinceNotSpecified
, can be used to adjust the number of items returned in one response. If the optional 'since' or 'until' range parameters are available but not used, then ApiResultLimitSinceNotSpecified
determines the results limit. Otherwise, if you do send 'since' or 'until' as query parameters, then ApiResultLimit
determines the results limit.
When the total number of results exceeds the limit, the response will contain an additional property called 'more':
"more":"Q5GITSQBAAAKAAAA"
If you use the original query parameters along with 'more' and its value on a subsequent GET request, you will receive the next page of results:
/api/v2/registrations?since=2020-11-02T19:52:00Z&more=Q5GITSQBAAAKAAAA
Resetting Learner Progress
There may be times when you need to reset a learner's registration state so that they basically start over fresh. This may be so the user can retake the course, or to clear out data during testing. You can trigger this clearing of progress data by doing a DELETE request to the /registrations/{registrationId}/progress endpoint.