Skip to content
This repository has been archived by the owner on Nov 30, 2021. It is now read-only.

Release 2.0.0 #6

Merged
merged 22 commits into from
Oct 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .yo-rc.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
"generator-phovea": {
"type": "lib-slib",
"modules": [
"tdp_core",
"tdp_core"
],
"libraries": [],
Expand All @@ -21,4 +20,4 @@
"ignores": [],
"extensions": []
}
}
}
27 changes: 16 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,35 @@ Matomo tracking for TDP applications based on provenance graph commands.
Configuration
------------

The tracking starts when a URL to a Matomo backend is set in the `config.js`.
The site ID corresponds with the Matomo site.
* The tracking starts when a URL to a Matomo backend is set in the `config.js`.
* The site ID corresponds with the Matomo site.
* Enable the [md5](https://en.wikipedia.org/wiki/MD5) encryption of user names to prevent plaintext logging (e.g., when using Matomo with LDAP login)

```js
{
"matomo": {
"url": "https://matomo.my-example-domain.com/", // matomo url with a trailing slash
"site": "1"
"site": "1",
"encryptUserName": false
}
}
```

### Provenance Commands

The tracked default provenance commands from [tdp_core](https://github.com/datavisyn/tdp_core) are defined in [actions.ts](./src/actions.ts).

Add a list of custom events when initializing the tracking:
Provenance commands using the extension point `actionFunction` must be annotated with the property `tdp_matomo` in order to be found and tracked.
The `tdp_matomo` configuration property requires the properties `category` and `action` from the `IMatomoEvent` (in *src/matomo.ts*), which can contain arbitrary strings.

```ts
const trackableActions: ITrackableAction[] = [
// id = phovea extension id
{id: 'targidCreateView', event: {category:'view', action: 'create'}},
];
trackApp(app, trackableActions);
registry.push('actionFunction', 'targidCreateView', function() {
return System.import('./internal/cmds');
}, {
factory: 'createViewImpl',
tdp_matomo: {
category: 'view',
action: 'create'
}
});
```


Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "tdp_matomo",
"description": "Matomo tracking for TDP applications",
"homepage": "https://datavisyn.io",
"version": "1.0.0",
"version": "2.0.0",
"author": {
"name": "datavisyn GmbH",
"email": "[email protected]",
Expand Down Expand Up @@ -108,6 +108,7 @@
"ts-loader": "4.0.1"
},
"dependencies": {
"tdp_core": "github:datavisyn/tdp_core#semver:^5.2.0"
"crypto-js": "^3.1.9-1",
"tdp_core": "github:datavisyn/tdp_core#semver:^5.5.0"
}
}
16 changes: 0 additions & 16 deletions phovea.js

This file was deleted.

6 changes: 3 additions & 3 deletions phovea_registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
**************************************************************************** */

import {register} from 'phovea_core/src/plugin';

import reg from './src/phovea';
/**
* build a registry by registering all phovea modules
*/
//other modules
import 'tdp_core/phovea_registry.js';
import 'tdp_core/phovea_registry.js';

//self
register('tdp_matomo',require('./phovea.js'));
register('tdp_matomo', reg);
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
-e git+https://github.com/datavisyn/tdp_core.git@develop#egg=tdp_core
-e git+https://github.com/datavisyn/tdp_core.git@v5.5.0#egg=tdp_core
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#!/usr/bin/env python
###############################################################################
# Caleydo - Visualization for Molecular Biology - http://caleydo.org
# Copyright (c) The Caleydo Team. All rights reserved.
Expand Down
27 changes: 0 additions & 27 deletions src/actions.ts

This file was deleted.

167 changes: 85 additions & 82 deletions src/matomo.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import {on} from 'phovea_core/src/event';
import {GLOBAL_EVENT_USER_LOGGED_IN, IUser, GLOBAL_EVENT_USER_LOGGED_OUT} from 'phovea_core/src/security';
import {IUser} from 'phovea_core/src/security';
import {ProvenanceGraph, ActionNode} from 'phovea_core/src/provenance';
import {getAPIJSON} from 'phovea_core/src/ajax';
import ATDPApplication from 'tdp_core/src/ATDPApplication';
import {trackableActions} from './actions';
import {list} from 'phovea_core/src/plugin';
import md5 from 'crypto-js/md5';

/**
* Trackable Matomo event
Expand Down Expand Up @@ -44,9 +43,44 @@ interface IPhoveaMatomoConfig {
* ID of the Matomo site (generated when creating a page)
*/
site: string;

/**
* Flag whether the user name should be encrypted using MD5 or not
*/
encryptUserName?: boolean;
}

const matomo = {
class Matomo {

private userId: string;

init(config: IPhoveaMatomoConfig) {
if (!config.url) {
return false;
}

const userId = (config.encryptUserName === true) ? md5(this.userId).toString() : this.userId;

_paq.push(['setUserId', userId]);

// _paq.push(['requireConsent']); TODO user consent form with opt out
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
// enable correct measuring of the site since it is a single page site
_paq.push(['enableHeartBeatTimer']);

_paq.push(['setTrackerUrl', `${config.url}matomo.php`]);
_paq.push(['setSiteId', config.site]);

const s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.defer = true;
s.src = `${config.url}matomo.js`;
const base = document.getElementsByTagName('script')[0];
base.insertAdjacentElement('beforebegin', s);
}

trackEvent(category: string, action: string, name?: string, value?: number) {
const t: any[] = ['trackEvent', category, action];
if (typeof name === 'string') {
Expand All @@ -56,22 +90,53 @@ const matomo = {
t.push(value);
}
_paq.push(t);
},
login(user: string) {
_paq.push(['setUserId', user]);
// _paq.push(['requireConsent']); TODO user consent form with opt out
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
// enable correct measuring of the site since it is a single page site
_paq.push(['enableHeartBeatTimer']);
},
}

login(userId: string) {
// store for later as we need to wait for the config to know whether the user name should be encrypted or not
this.userId = userId;
}

logout() {
_paq.push(['resetUserId']);
_paq.push(['trackPageView']);
}
};
}

const matomo = new Matomo();

/**
* Login extension point
*/
export function trackLogin(user: IUser) {
matomo.login(user.name);
}

/**
* Logout extension point
*/
export function trackLogout() {
matomo.logout();
}

/**
* Provenance graph extension point
* @param graph ProvenanceGraph
*/
export async function trackProvenance(graph: ProvenanceGraph) {
if (graph.isEmpty) {
matomo.trackEvent('session', 'new', 'New Session');
} else {
matomo.trackEvent('session', 'continue', `${graph.desc.id} at state ${Math.max(...graph.states.map((s) => s.id))}`);
}

const trackableActions = new Map<string, IMatomoEvent>();

// load all registered actionFunction extension points and look if they contain a `analytics` property
list((desc) => desc.type === 'actionFunction' && desc.analytics).forEach((desc) => {
trackableActions.set(desc.id, desc.analytics);
});

function trackGraph(graph: ProvenanceGraph, trackableActions: Map<string, IMatomoEvent>) {
graph.on('execute', (_, node: ActionNode) => {
if(!Array.from(trackableActions.keys()).includes(node.getAttr('f_id'))) {
return;
Expand All @@ -84,73 +149,11 @@ function trackGraph(graph: ProvenanceGraph, trackableActions: Map<string, IMatom
(typeof event.value === 'function') ? event.value(node) : null
);
});
graph.on('run_chain', (_, nodes: ActionNode[]) => {
const event = trackableActions.get('runChain');
matomo.trackEvent(event.category, event.action, 'Run actions in chain', nodes.length);
});
}

function initMamoto(config: IPhoveaMatomoConfig): boolean {
if (!config.url) {
return false;
}
_paq.push(['setTrackerUrl', `${config.url}matomo.php`]);
_paq.push(['setSiteId', config.site]);

const s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.defer = true;
s.src = `${config.url}matomo.js`;
const base = document.getElementsByTagName('script')[0];
base.insertAdjacentElement('beforebegin', s);
return true;
}

/**
* Track provenance commands of any TDPApplication
*
* Add custom actions using the phovea extension id:
*
* ```ts
* // id = phovea extension id
* const trackableActions: ITrackableAction[] = [
* {id: 'targidCreateView', event: {category:'view', action: 'create'}},
* ];
* trackApp(app, trackableActions);
* ```
*
* @param tdpApp ATDPApplication<any>
* @param customActions List of custom actions
*/
export function trackApp(tdpApp: ATDPApplication<any>, customActions?: {id: string, event: IMatomoEvent}[]): Promise<boolean> {
// merge custom actions into trackable actions
if(customActions && customActions.length > 0) {
customActions.forEach((action) => trackableActions.set(action.id, action.event));
}

const matomoConfig = getAPIJSON('/tdp/config/matomo');

tdpApp.on(ATDPApplication.EVENT_OPEN_START_MENU, () => matomo.trackEvent('startMenu', 'open', 'Open start menu'));

on(GLOBAL_EVENT_USER_LOGGED_IN, (_, user: IUser) => {
matomo.login(user.name);
tdpApp.graph.then((graph) => {
if (graph.isEmpty) {
matomo.trackEvent('session', 'new', 'New Session');
} else {
matomo.trackEvent('session', 'continue', `${graph.desc.id} at state ${tdpApp.clueManager.storedState || Math.max(...graph.states.map((s) => s.id))}`);
}

matomoConfig.then((config: IPhoveaMatomoConfig) => {
trackGraph(graph, trackableActions);
});
});
});

on(GLOBAL_EVENT_USER_LOGGED_OUT, () => {
matomo.logout();
graph.on('run_chain', (_, nodes: ActionNode[]) => {
matomo.trackEvent('provenance', 'runChain', 'Run actions in chain', nodes.length);
});

return matomoConfig.then((config: IPhoveaMatomoConfig) => initMamoto(config));
const config: IPhoveaMatomoConfig = await getAPIJSON('/tdp/config/matomo');
matomo.init(config);
}
26 changes: 26 additions & 0 deletions src/phovea.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/* *****************************************************************************
* Caleydo - Visualization for Molecular Biology - http://caleydo.org
* Copyright (c) The Caleydo Team. All rights reserved.
* Licensed under the new BSD license, available at http://caleydo.org/license
**************************************************************************** */
import {IRegistry} from 'phovea_core/src/plugin';

// TODO: Use these constants for extension types below. Waiting for `tdp_core` to point to `phovea_clue` develop branch again (see https://github.com/datavisyn/tdp_core/blob/develop/package.json#L82).
// import {EP_PHOVEA_CORE_LOGIN, EP_PHOVEA_CORE_LOGOUT} from 'phovea_core/src/extensions';
// import {EP_PHOVEA_CLUE_PROVENANCE_GRAPH} from 'phovea_clue/src/extensions';

export default function (registry: IRegistry) {

registry.push('epPhoveaCoreLogin', 'matomoLogin', () => System.import('./matomo'), {
factory: 'trackLogin'
});

registry.push('epPhoveaCoreLogout', 'matomoLogout', () => System.import('./matomo'), {
factory: 'trackLogout'
});

registry.push('epPhoveaClueProvenanceGraph', 'matomoAnalytics', () => System.import('./matomo'), {
factory: 'trackProvenance'
});

}
3 changes: 2 additions & 1 deletion tdp_matomo/config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"matomo": {
"url": "", // matomo url with a trailing slash
"site": "1"
"site": "1",
"encryptUserName": false
}
}
Loading