Metrics

Problem

You want your plugin to show up in the “Capabilities” dialog to help users discover your plugin features.

../../_images/capabilitiesdlg_orig.png

Note

This is available from the Support menu, in the top right corner of the main window.

../../_images/supportmenudlg.png

Solution

Use the metrics API to provide your plugin specific functionality.

Metrics Cookbook example - menu creation
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
goog.declareModuleId('plugin.cookbook_metrics.CookbookMetrics');

import MetricsPlugin from 'opensphere/src/os/ui/metrics/metricsplugin.js';
import {Metrics} from './index.js';

/**
 */
export default class CookbookMetrics extends MetricsPlugin {
  /**
   * Constructor.
   */
  constructor() {
    super();

    this.setLabel('Cookbook');
    this.setIcon('fa fa-book');
    this.setCollapsed(true);
    this.setDescription('Plugin for metrics example.');

    const leaf = this.getLeafNode();
    this.addChild(leaf, {
      label: 'First Metric Item',
      description: 'This is an item.',
      key: Metrics.FIRST_THING
    });

    this.addChild(leaf, {
      label: 'Second Metric Item',
      description: 'Does something.',
      key: Metrics.SECOND_THING
    });

    this.addChild(leaf, {
      label: 'Third Metric Item',
      description: 'Combination, programmatic.',
      key: Metrics.EXTRA_THING
    });
  }
}

The dialog will then have additional items

../../_images/capabilitiesdlg_cookbook.png

Discussion

The metrics API can be considered in two parts:

  • setting up the structure that represents what you want to track in terms of usage
  • updating the usage tracking when a particular feature is used

The code shown above sets up the structure. Please ensure that your usage is consistent with the way other features are shown in the dialog.

Updating the usage so that the dialog shows a tick (check mark) rather than a cross obviously depends on the how the feature works. However a very common case is when the user makes a menu selection, and this is very easy to support by adding a metricKey entry to your menu definition (see Submenu cookbook sample for more on menus).

group.addChild({
  type: MenuItemType.ITEM,
  eventType: EventType.DO_ANOTHER_THING,
  label: 'Item 1',
  metricKey: Metrics.FIRST_THING,
  handler: handleItem
});

When the user selects that menu item, the corresponding metric entry will be set.

Some features are less suited to just triggering off a menu item, and a programmatic approach is better. For example, the EXTRA_THING key could be triggered by using code like:

OSMetrics.getInstance().updateMetric(Metrics.EXTRA_THING, 1);

In the example, this is triggered by either of the menu items (since the handler is shared). However a real usage would likely have it matching some specific user behaviour.

Full code

Metrics Cookbook Example - src/index.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
goog.declareModuleId('plugin.cookbook_metrics');

import OSMetrics from 'opensphere/src/os/metrics/metrics.js';

/**
 * @type {string}
 */
export const ID = 'cookbook_metrics';

/**
 * @type {string}
 */
export const MYGROUP = 'Cookbook Group';

/**
 * Our event types
 * @enum {string}
 */
export const EventType = {
  DO_SOMETHING: 'cookbook:do_y',
  DO_ANOTHER_THING: 'cookbook:do_x'
};

export const Metrics = {
  FIRST_THING: 'Metric for First Item',
  SECOND_THING: 'Metric for Second Item',
  EXTRA_THING: 'Metric for programmatic item'
};

/**
 * Process a menu item
 * @param {os.ui.menu.MenuEvent} event The menu event.
 */
export const handleItem = function(event) {
  alert('item selected:' + event.type);
  OSMetrics.getInstance().updateMetric(Metrics.EXTRA_THING, 1);
};
Metrics Cookbook Example - src/cookbookmetrics.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
goog.declareModuleId('plugin.cookbook_metrics.CookbookMetrics');

import MetricsPlugin from 'opensphere/src/os/ui/metrics/metricsplugin.js';
import {Metrics} from './index.js';

/**
 */
export default class CookbookMetrics extends MetricsPlugin {
  /**
   * Constructor.
   */
  constructor() {
    super();

    this.setLabel('Cookbook');
    this.setIcon('fa fa-book');
    this.setCollapsed(true);
    this.setDescription('Plugin for metrics example.');

    const leaf = this.getLeafNode();
    this.addChild(leaf, {
      label: 'First Metric Item',
      description: 'This is an item.',
      key: Metrics.FIRST_THING
    });

    this.addChild(leaf, {
      label: 'Second Metric Item',
      description: 'Does something.',
      key: Metrics.SECOND_THING
    });

    this.addChild(leaf, {
      label: 'Third Metric Item',
      description: 'Combination, programmatic.',
      key: Metrics.EXTRA_THING
    });
  }
}
Metrics Cookbook Example - src/cookbookmetricsplugin.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
goog.declareModuleId('plugin.cookbook_metrics.CookbookMetricsPlugin');

import AbstractPlugin from 'opensphere/src/os/plugin/abstractplugin.js';
import PluginManager from 'opensphere/src/os/plugin/pluginmanager.js';
import MenuItemType from 'opensphere/src/os/ui/menu/menuitemtype.js';
import * as spatial from 'opensphere/src/os/ui/menu/spatial.js';
import MetricsManager from 'opensphere/src/os/ui/metrics/metricsmanager.js';

import CookbookMetrics from './cookbookmetrics.js';
import {ID, MYGROUP, EventType, Metrics, handleItem} from './index.js';


/**
 * Cookbook example of metrics
 */
export default class CookbookMetricsPlugin extends AbstractPlugin {
  /**
   * Constructor.
   */
  constructor() {
    super();
    this.id = ID;
    this.errorMessage = null;
  }

  /**
   * @inheritDoc
   */
  init() {
    const metricsManager = MetricsManager.getInstance();
    metricsManager.addMetricsPlugin(new CookbookMetrics());

    const menu = spatial.getMenu();
    if (menu) {
      const root = menu.getRoot();
      let group = root.find(MYGROUP);
      if (!group) {
        group = root.addChild({
          type: MenuItemType.GROUP,
          label: MYGROUP,
          tooltip: 'Added by cookbook metrics example'
        });
        group.addChild({
          type: MenuItemType.ITEM,
          eventType: EventType.DO_ANOTHER_THING,
          label: 'Item 1',
          metricKey: Metrics.FIRST_THING,
          handler: handleItem
        });
        group.addChild({
          type: MenuItemType.ITEM,
          eventType: EventType.DO_SOMETHING,
          label: 'Item 2',
          metricKey: Metrics.SECOND_THING,
          handler: handleItem
        });
      }
    }
  }
}

// add the plugin to the application
PluginManager.getInstance().addPlugin(new CookbookMetricsPlugin());