Skip to content

Instantly share code, notes, and snippets.

@lsloan
Last active September 13, 2023 14:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lsloan/3a0895cc97bff7373737e7c6b8c6bdad to your computer and use it in GitHub Desktop.
Save lsloan/3a0895cc97bff7373737e7c6b8c6bdad to your computer and use it in GitHub Desktop.
Caliper: An example of using caliper-php to send an event with custom action and context.
<?php
require_once 'vendor/autoload.php';
use IMSGlobal\Caliper\actions\Action;
use IMSGlobal\Caliper\Client;
use IMSGlobal\Caliper\context\Context;
use IMSGlobal\Caliper\entities\agent\Person;
use IMSGlobal\Caliper\entities\agent\SoftwareApplication;
use IMSGlobal\Caliper\entities\media\MediaLocation;
use IMSGlobal\Caliper\entities\media\VideoObject;
use IMSGlobal\Caliper\events\MediaEvent;
use IMSGlobal\Caliper\Options;
use IMSGlobal\Caliper\request\HttpRequestor;
use IMSGlobal\Caliper\Sensor;
use IMSGlobal\Caliper\util;
class CustomAction extends Action {
const
STREAMED_TO_TIME = 'StreamedToTime';
}
// In order for UDP to accept a custom action, a custom context must be used.
// NOTE: If an array of context URIs is used, the serialization code in
// caliper-php gives warnings, like…
// `PHP Warning: Array to string conversion in /umich-caliper-php/src/util/BasicEnum.php on line 104`
// These are harmless, but annoying. This will be addressed in a future
// release of caliper-php.
// As a workaround to avoid warnings, use a single URI instead of
// multiple. A single, non-standard URI is good enough to indicate to UDP
// that this event is special and UDP will accept the custom action.
// Technically, the multiple context should be used here.
class CustomContext extends Context {
const
SINGLE = 'http://example.edu/caliper/context/v1p1_custom.json',
MULTIPLE = [
parent::CONTEXT,
self::SINGLE],
__default = self::SINGLE;
}
date_default_timezone_set(
DateTimeZone::listIdentifiers(DateTimeZone::UTC)[0]);
$sensorId = 'umich_caliper_example';
$options = (new Options())
->setApiKey('super_secret_key')
->setDebug(true)
->setHost('https://lti.tools/caliper/event?key=' . $sensorId);
$sensor = (new Sensor($sensorId))
->registerClient('http',
new Client('clientId',
$options));
$video = (new VideoObject('https://example.edu/videos/' .
util\UuidUtil::makeUuidV4()))
->setDuration('PT59M59S')
->setName('A Highly Important Instructional Video')
->setDateCreated(new DateTime('2016-05-19'));
// Current minute and seconds as pseudorandom video time
$currentVideoTime = date('\P\Ti\Ms\S');
$event = (new MediaEvent())
// Using a subclass of Context seems like a complicated way to set context.
// Ideally, the context of events should be based on the context(s)
// of their entities.
->setContext(new CustomContext())
// Example of using multiple contexts.
// ->setContext(new CustomContext(CustomContext::MULTIPLE))
// Ideally, caliper-php should also set context on actions, but it
// doesn't. If it did, the event context could be based on the action
// context, as described above.
->setAction(new CustomAction(CustomAction::STREAMED_TO_TIME))
->setActor(new Person('https://example.edu/user/' .
get_current_user()))
->setEventTime(util\TimestampUtil::makeDateTimeWithSecondsFraction())
->setObject($video)
// The intent of the `StreamedToTime` custom action is to mark when
// the media playback reaches a quartile. There aren't any Caliper
// media entities which encode the percentage of playback (yet),
// so it can be encoded in an `extensions` property of an appropriate
// entity for now.
->setTarget(
(new MediaLocation($video->getId() . '#currentTime=' . $currentVideoTime))
->setIsPartOf($video->makeReference())
->setCurrentTime($currentVideoTime)
->setExtensions([
'percentage' => 0.25,
])
)
->setEdApp((new SoftwareApplication('https://example.edu/video_player'))
->setVersion('1.2')
->setDateCreated(new DateTime('2015-02-09T12:25'))
);
/*
* NOTE: The next few blocks of code are for debugging and demonstration
* purposes. It is not necessary for sending events.
*/
// Makes JSON for event, but includes attributes without values
// print json_encode($event->jsonSerialize(),
// $options->getJsonEncodeOptions()) . PHP_EOL;
// Requestor-based serialization removes attributes without values and
// includes an envelope.
$requestor = new HttpRequestor($options);
$envelope = $requestor->createEnvelope($sensor, $event);
// Using `@` here suppresses warnings about context array instead of string.
// However, all other warnings are also suppressed, so be careful.
// It may be better to use a single string context instead of an array.
$payload = $requestor->serializeData($envelope);
print $payload . PHP_EOL;
/*
* End of debugging and demonstration code.
*/
// As above, `@` suppresses warnings about multiple contexts. Caveat emptor.
try {
@$sensor->send($sensor, $event);
} catch (\RuntimeException $sendException) {
echo 'Error sending event: ' . $sendException->getMessage() . PHP_EOL;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment