Topspin tracker usage

Topspin tracker usage

This chapter focuses on the technical implementation of the Topspin tracker, with platform specific code examples on the right hand side of the page.

Initialising the Topspin tracker

/* The integration follows the Topspin API layer in the browser. 
This layer is placed by the platform developer. 
The platform developer makes sure the Topspin tracker is initialised 
with the appropriate label values (see previous section for more details 
on the labels and corresponding values). Below is an example.
 */

<script type="text/javascript">
  topspinLayer = {
    comScoreName: 'vroegevogels.ps.media.358019.pagina',
    contentId: 'crid://vara.nl/cms/episode/358019', // Optional
    brand: 'vroegvogels',
    section: 'ps',
    broadcaster: 'bnnvara',
    subBroadcaster: 'vara',
    brandType: 'programma',
    avType: 'audio', // Optional
    channel: 'nporadio1', // Optional
    user: 'foob-1234-arfo', // Optional
    profile: '1234-abcd-5678', // Optional
    platform: 'site',
    platformType: 'site'
  };
</script>

// Once the Topspin Layer is placed can be loaded at the end of the page as such:
<script src="//topspin.npo.nl/tt/topspin.js"></script>
// The Topspin Tracker can be integrated as a cocoapod b putthing the following line in the Podfile:
pod 'Topspin', :git => 'https://github.com/npo-topspin/topspin-tag-ios-release.git',
:tag => '1.2.0'

// Integration into the iOS app:

TSTopspinFactory* factory = [[TSTopspinFactory alloc] init];
TSTopspin* topspin = [factory build];
TSPageLabels* labels = [topspin pageLabels];
labels.brand = @"your-own-npo-brand-here";
labels.brandType = TSEventBrandTypeBroadcastPortal;
labels.broadcaster = @"enter-appropriate-broadcaster-here";
 
// To disable logs:
[TSTopspin enableLogs:NO];

// To register the app to automatically send app-lifecycle events:
[topspin registerAppLifecycle];

// This creates a Topspin client with all default settings. The first time it’s used, it will generate 
// a Divolte cookie ID and store it in the standard user defaults. Upon subsequent use, it will load this 
// cookie ID from the user defaults.
// For testing purposes it can be useful to disable network calls. Use to following to disable network calls:

[[TSTopspin sharedInstance] disableNetworkCalls:YES];
//  Make sure your app has Internet permissions
// Add the Topspin library as a dependency of your application as follows;

repositories {
    maven {
        url "http://npo-topspin.bintray.com/public"
        credentials {
            username '<USERNAME>'
            password '<API_KEY>'
         }
     }
}

dependencies {
    compile 'nl.npo.topspin:topspin-android:1.4.0'
}

Initialising the topspin tracker is easy, just follow the below steps for your platform of choice. If you require more context for how to set a field with it’s appropriate value, please refer to the data context

Type of events

In this section we will guide you through the different possible event measurements, how to set them up, and what additional parameters to pass along.

pageView

A pageView only really exists in browser land as the concept doesn’t hold within an app. A pageView event is generated every time a new page is loaded. This functionality is automatically activated when the Topspin tracker JavaScript version is implemented.

Send a pageView event

A pageView is automatically implemented when the Topspin Tracker is loaded.
The concept of a pageView is not relevant for a mobile application and is thus not implemented. See contentView instead. 
The concept of a pageView is not relevant for a mobile application and is thus not implemented. See contentView instead.

contentView

For mobile devices we register a contentView instead of a pageView.

Send a contentView event

The concept of a contentView is not implemented in the javaScript Topspin Tracker. See pageView instead.
// A tracker object must be created to track events:
TSTracker* tracker = [[TSTopspin sharedInstance] createNewTracker];
tracker.section = @"navigation";
tracker.comscoreName = @"tester.navigation";
tracker.subBroadcaster = @"example.broadcaster";

// To send a contentView:
tracker.contentId = @"crid://your/url/scheme/here"; // see contentId
[[tracker contentView] send];
public class MainActivity extends Activity {

   Tracker mTracker;
   
   @Override
   protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      
      // page view, inheriting global labels from app
      mTracker = TopspinInstance.get()
         .createTracker()
         // set required labels for a page
         .setSection("start") // example value
         .setContentId("crid://startpagina") // example value
         .setComscoreName("pagina.start") // example value
         .setSubBroadcaster(""); // example value
      
      // perform actual pageview
      mTracker.contentView()
         // optional: referrer
         .setReferrer("homescreen") // example value
         .send();
   }
}

player events

From the player we receive a set of events, such as a streamStart for every time a player/stream is started. Other possibilities include streamPause, streamWaypoint and streamComplete.

Send player events

to be implemented
to be implemented
// To start tracking streaming (player) events, first initialise a tracker object:
- (void)viewDidLoad {
    [super viewDidLoad];
    _tracker = [[TSTopspin sharedInstance] createNewTracker];
    _tracker.section = @"playback";
    _tracker.contentID = [NSString stringWithFormat:@"%@/player/%@", TSDemoBaseCrid,
    @"someContentID"];
    _tracker.comscoreName = @"tester.player";
    _tracker.subBroadcaster = @"example.broadcaster";
    ...
}

// Send the events as follows:
- (void)startPlayback {
    _streamTracker = [[TSStreamTracker alloc] initWithTracker:_tracker
    mid:@"KNO_2308383"];
    [_streamTracker load];
}

- (void)pause {
    [_avPlayer pause];
    CMTime position = [_avPlayer currentTime];
    [_streamTracker pause:CMTimeGetSeconds(position)];
}

Search events are gathered from queries to the search box, there are two possible scenarios:

  • A quick search function, which starts “auto-suggesting” content as soon as a user starts typing. A user can click on content to directly navigate to the series overview.
  • If the suggested content from the quick search is not satisfactory, a user can click ‘all results’ to continue to the advanced search.

A quick search triggers offer events everytime content is suggested from the box, along with a corresponding choice event if the user clicks such a suggestion. Each new set of suggestions trigger their own set of offer events, with the query text as “source”

Send search events

<!-- quick search example using auto-suggest, searchterm = 'lui' -->
<ul data-ts-panel="auto-suggest" data-ts-source="lui">
   <li>
      <a title="Kijkenluisteren" href="https://npo.nl/kijkenluisteren/POMS_S_NTR_360196" data-ts-offer data-ts-search
      <div class="npo-asset-tile-container asset-image">
         <div class="npo-asset-tile">
            <div class="npo-asset-tile-image"></div>
         </div>
         <div class="npo-asset-tile-title">
            <h2>Kijkenluisteren</h2>
            <p></p>
         </div>
      </div>
      </a>
   </li>
   <li>
      <a title="De Luizenmoeder" href="https://npo.nl/de-luizenmoeder/AT_2072508" data-ts-offer data-ts-search="auto-
         <div class="npo-asset-tile-container asset-image">
         <div class="npo-asset-tile">
            <div class="npo-asset-tile-image" style="background-image: url('https://www-assets.npo.nl/uploads/m
               </div>
               <div class="npo-asset-tile-title">
               <h2>De Luizenmoeder</h2>
               <p></p>
            </div>
         </div>
      </a>
   </li>
   etc... 
</ul>

To clarify with an example; a user is looking for “Zondag met Lubach” so starts typing “zon” in the searchbox. This triggers offer events for the suggestions displayed. The user continues typing “zondag” which triggers another query to the search engine, and yet another set of offer events. Finally the user selects a suggestion, resulting in a choice event for the specific content.

The second scenario is when a user does not use the quick search feature and presses the “Alle resultaten” button. This will navigate to a page with two lanes; “Programma’s” and “Afleveringen”. Both these lanes should be registered as invididual search events, with their own panel name and index, along with the query text.

<!-- an extended search example, searchterm = 'lui' -->
<div id="component-franchises" data-ts-panel="search-franchise" data-ts-source="lui">
   <input type="hidden" name="nextLink" value="">
   <input type="hidden" name="selfLink"
      value="/search/extended?pageSize=10&amp;query=lui&amp;filter=programs&amp;dateFrom=2014-01-01">
   <input type="hidden" name="tileType" value="teaser">
   <input type="hidden" name="tileMapping" value="normal">
   <input type="hidden" name="pageType" value="search">
   <div class="npo-grid-teaser">
      <div class="npo-ankeiler-tile-container npo-ankeiler-tile-weblink npo-tile" id="VPWON_1271886">
         <a href="https://npo.nl/danny-zoekt-stemmers/VPWON_1271886" title="Danny zoekt stemmers"
            class="npo-ankeiler-tile"
            data-scorecard="{&quot;name&quot;:&quot;collecties.programmas.danny-zoekt-stemmers.VPWON_1271886&quot;}"
            data-ts-offer
            data-ts-search="search-franchise-base"
            data-ts-destination="VPWON_1271886">
            <div class="npo-ankeiler-tile-image"
               style="background-image: url('https://images.poms.omroep.nl/image/s320/c320x180/870521.jpg');">
               <h2>Danny zoekt stemmers</h2>
            </div>
         </a>
      </div>
      <div class="npo-ankeiler-tile-container npo-ankeiler-tile-weblink npo-tile" id="POMS_S_NPO_4008736">
         <a href="https://npo.nl/speellijst/POMS_S_NPO_4008736" title="Discriminatie, racisme en uitsluiting " class="npo-ankeiler-tile
         data-scorecard="{&quot;name&quot;:&quot;collecties.programmas.discriminatie-racisme-en-uitsluiting.POMS_
         data-ts-offer
         data-ts-search="search-franchise-base"
         data-ts-destination="POMS_S_NPO_4008736">
         <div class="npo-ankeiler-tile-image">
            <h2>Discriminatie, racisme en uitsluiting </h2>
         </div>
         </a>
      </div>
      etc...
   </div>
   <!-- episodes blok -->
   <div id="component-episodes" data-ts-panel="search-episode" data-ts-source="lui">
      <input type="hidden" name="nextLink"
         value="/search/extended?pageSize=10&amp;page=2&amp;query=lui&amp;filter=episodes&amp;dateFrom=2014-01-01">
      <input type="hidden" name="selfLink"
         value="/search/extended?pageSize=10&amp;query=lui&amp;filter=episodes&amp;dateFrom=2014-01-01">
      <input type="hidden" name="tileType" value="asset">
      <input type="hidden" name="tileMapping" value="search">
      <input type="hidden" name="pageType" value="search">
      <div class="npo-grid-asset">
         <div class="npo-asset-tile-container npo-tile   " id="POW_03205575">
            <a href="https://npo.nl/tip-de-muis/22-03-2018/POW_03205575" class="npo-tile-link" data-radio=""
               data-mediatarget="" data-scorecard="{&quot;name&quot;:&quot;zoeken.tip-de-muis.POW_03205575&quot;}"
               data-order=""
               data-ts-offer
               data-ts-search="search-episode-base"
               data-ts-destination="POW_03205575">
               <div class="npo-asset-tile ">
                  <div class="npo-asset-tile-availability" data-from="2018-03-22T12:36:20Z"
                     data-to="2018-03-29T11:36:20Z" data-prediction="" data-premium-from="2018-03-22T12:36:20Z"
                     data-premium-to="" data-premium-prediction="" style="display: block;">Nog 2 uur beschikbaar
                  </div>
                  <div class="npo-asset-tile-timer">
                     7 min
                  </div>
                  <div class="npo-asset-tile-image">
                     <img src="https://images.poms.omroep.nl/image/s320/c320x180/1028211.jpg"
                        alt="Tip de muis: Wat een moeite!" onerror="this.src='/images/tile-placeholder.png'"
                        class="thumb-fallback">
                  </div>
                  <div class="npo-asset-tile-new">nieuw</div>
                  <div class="npo-asset-tile-play"></div>
                  <div class="npo-asset-tile-watched">bekeken</div>
                  <div class="npo-asset-tile-progress">
                     <div class="npo-asset-tile-progress-bar" style="width: %"></div>
                  </div>
               </div>
               <h2>Tip de muis: Wat een moeite!</h2>
               <p>Afl. 16 - Do 22 mrt 13:30</p>
            </a>
         </div>
         <div class="npo-asset-tile-container npo-tile
         <a href="https://npo.nl/koek-ei-op-safari/20-08-2014/VPWON_1227223" class="npo-tile-link" data-radio=""
            data-mediatarget=""
            data-scorecard="{&quot;name&quot;:&quot;zoeken.koek-ei-op-safari.VPWON_1227223&quot;}" data-order=""
            data-ts-offer
            data-ts-search="search-episode-base"
            data-ts-destination="VPWON_1227223">
            <div class="npo-asset-tile ">
               <div class="npo-asset-tile-availability" data-from="" data-to="" data-prediction=""
                  data-premium-from="2017-03-29T00:00:00Z" data-premium-to="2017-03-29T00:00:00Z"
                  data-premium-prediction=""></div>
               <div class="npo-asset-tile-timer">
                  7 min
               </div>
               <div class="npo-asset-tile-image">
                  <img src="https://images.poms.omroep.nl/image/s320/c320x180/502308.jpg"
                     alt="Koek &amp; Ei op safari: De vermorzeldame"
                     onerror="this.src='/images/tile-placeholder.png'" class="thumb-fallback">
               </div>
               <div class="npo-asset-tile-new">nieuw</div>
               <div class="npo-asset-tile-play"></div>
               <div class="npo-asset-tile-watched">bekeken</div>
               <div class="npo-asset-tile-progress">
                  <div class="npo-asset-tile-progress-bar" style="width: %"></div>
               </div>
            </div>
            <h2>Koek &amp; Ei op safari: De vermorzeldame</h2>
            <p>Afl. 3 - 20 aug 2014 11:01</p>
         </a>
      </div>
      etc...
   </div>
</div>

player recommendation

The player shows a set of related recommendations when pausing the stream on a full-screen view (except on smaller mobile screens). These items are gathered as recommendation offer events, with as source the streamid that is currently being played. These events should pass through the recommender (name) from the api response.

personal recommendation

Personal recommendations are currently only available as lane on the homepage. When this lane is visible in the screen, it should fire offer / choice events just like the other lanes. It should provide the recommender name as given by the api response.

lane recommendation (NPO Start only)

For NPO Start we have included the possibility to collect user data on the different lanes present on the portal. Showing an item from a lane to a user is considered a recommendation. Clicking one of these items is thus considered a choice. We need to register the type of lane, the type of page it is shown on, the type of lane it is shown in and the position of the lane in the form of an index. Let’s run through the different optional values for these fields.

page type

The type of the page is important to register, because a specific lane can sometimes be found on different types of pages. At the time of writing we register the following possible values for pageType):

  • home, for the homepage of NPO Start;
  • series, for a series page;
  • collection, for a collection page.

lane type

Currently we identify two different types of lanes on NPO Start, the normal lane, and a lane that holds five items of different size. The appropriate values for these different lane types are:

  • regular, for the normal lane;
  • 5grid, for the lane that holds five items of different sizes.

position of the lane

The position of the lane is important to infer how far down a user scrolls before the appropriate content is found. The index starts counting at 0 (zero), so the top most lane will have an index of zero. The second lane will have an index of 1, etc.

source of the recommendation

It is also important to indicate with what content the recommendations are offered. We refer to this as the source of the recommendation.

The above information is provided by the NPO Start API, and accessible via the field name of a component named panel.

This needs to explain how to link an Editorial tracker to the editorial offers, and what fields need to be passed where.
This needs to be implemented.
This needs to be implemented.

custom event

With the Topspin Tracker, it is possible to create hand-tailored events. As a simple example, we want to know how many users click a specific button on the homepage. Implementing a custom event type here, e.g. buttonClicked, would allow insights in which users click (think of registered vs. anonymous) the button and at what time of day.

This needs to explain how to implement custom events, maybe stick to the 'buttonClicked' example.
This needs to be implemented.
This needs to be implemented.