Skip to content

Commit

Permalink
Merge pull request #14 from uwcirg/feature/piwik-tracking
Browse files Browse the repository at this point in the history
add frontend piwik tracking code
  • Loading branch information
achen2401 authored Feb 5, 2025
2 parents 975007f + bc4bdae commit 9f3e624
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/ui-client/src/containers/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import DataImportContainer from '../containers/DataImport/DataImport';
import UserQuestionModal from './UserQuestionModal/UserQuestionModal';
import { SavedQueryMap } from '../models/Query';
import { sleep } from '../utils/Sleep';
import { addMatomoTracking } from '../utils/piwik';
import NotificationModal from '../components/Modals/NotificationModal/NotificationModal';
import MaintainenceModal from '../components/Modals/MaintainenceModal/MaintainenceModal';
import './App.css';
Expand Down Expand Up @@ -74,6 +75,7 @@ class App extends React.Component<Props> {
this.handleSessionTokenRefresh();
dispatch(getIdToken());
dispatch(refreshServerStateLoop());
addMatomoTracking();
}

public componentDidUpdate() {
Expand Down
78 changes: 78 additions & 0 deletions src/ui-client/src/utils/envConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import Axios from "axios";
import { getAuthConfig, getUserTokenAndContext } from "../services/authApi";
interface EnvVar {
key: string;
value: string | object;
}

export async function fetchEnvData(): Promise<any> {
return new Promise((resolve) => {
// fetch data from appsettings.json and env.json
Promise.allSettled([
getAuthConfig().then((config) => config), // general config settings
getAuthConfig().then((config) => getUserTokenAndContext(config)), // user auth specific settings
Axios.get("/env.json"), // env.json, generated via script to be populated with environment variables
])
.then((results) => {
let returnResults = {};
if (results[0].value) {
returnResults = results[0].value;
}
if (results[1].value) {
returnResults = {
...returnResults,
// user auth/context info will be under the `user` key
user: results[1].value
}
}
if (results[2].value && results[2].value.data) {
returnResults = {
...returnResults,
...results[2].value.data
}
}
resolve(returnResults);
})
.catch((e) => resolve(null));
});
}
// data coming from appsettings && env.json
export async function getEnvData() {
const results = await fetchEnvData();
if (window && results) {
window["appConfig"] = results;
console.table("Environment variables:",getEnvs());
}
return results;
}
export function getEnv(key: string) {
//window application global variables
if (window && window["appConfig"] && window["appConfig"][key])
return window["appConfig"][key];
const envDefined = typeof process !== "undefined" && process.env;
//enviroment variables as defined by Node
if (envDefined && process.env[key]) return process.env[key];
return "";
}

export function getEnvs(): EnvVar[] {
let arrEnvs: EnvVar[] = [];
const BLACK_LIST = ["SECRET", "KEY", "TOKEN", "CREDENTIALS"];
if (window && window["appConfig"]) {
const keys = Object.keys(window["appConfig"]);
keys.forEach((key) => {
if (BLACK_LIST.indexOf(key.toUpperCase()) !== -1) return true;
arrEnvs.push({ key: key, value: window["appConfig"][key] });
});
}
const envDefined = typeof process !== "undefined" && process.env;
if (envDefined) {
const envKeys = Object.keys(process.env);
envKeys.forEach((key) => {
if (BLACK_LIST.indexOf(key.toUpperCase()) !== -1) return true;
arrEnvs.push({ key: key, value: process.env[key] });
});
}
console.log("Environment variables ", arrEnvs);
return arrEnvs;
}
55 changes: 55 additions & 0 deletions src/ui-client/src/utils/piwik.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import {getEnvData} from "./envConfig";

// get user Id from config, i.e. see getAuthConfig, getUserTokenAndContext from authAPI
export function getUserIdFromEnv(config) : number | string | null {
if (!config) return null;
if (config.user && config.user.name) return config.user.name;
if (config.profile) return config.profile;
if (config.fhirUser) return config.fhirUser;
if (config["preferred_username"]) return config["preferred_username"];
return null;
}
// get PIWIK siteId from environment variable
export function getMatomoSiteIdFromEnv(config) : number | null{
if (!config) return null;
const envs = config.client ? config.client: config;
// appsettings.json {client: .....}
// env.json
if (envs["REACT_APP_MATOMO_SITE_ID"]) return envs["REACT_APP_MATOMO_SITE_ID"];
if (envs["MATOMO_SITE_ID"]) return envs["MATOMO_SITE_ID"];
if (envs["REACT_APP_PIWIK_SITE_ID"]) return envs["REACT_APP_PIWIK_SITE_ID"];
if (envs["PIWIK_SITE_ID"]) return envs["PIWIK_SITE_ID"];
return envs["SITE_ID"];
}
export async function addMatomoTracking() {
// already generated script, return
if (!window || document.querySelector("#matomoScript")) return;
const envData = await getEnvData();
const userId = getUserIdFromEnv(envData);
console.log("PIWIK userId ", userId);
// no user Id return
if (!userId) return;
const siteId = getMatomoSiteIdFromEnv(envData);
console.log("PIWIK siteId ", siteId)
// no site Id return
if (!siteId) return;
// init global piwik tracking object
// note this will only be executed if BOTH userId and siteId are present
window._paq = [];
window._paq.push(["trackPageView"]);
window._paq.push(["enableLinkTracking"]);
window._paq.push(["setSiteId", siteId]);
window._paq.push(["setUserId", userId]);

let u = "https://piwik.cirg.washington.edu/";
window._paq.push(["setTrackerUrl", u + "matomo.php"]);
let d = document,
g = d.createElement("script"),
headElement = document.querySelector("head");
g.type = "text/javascript";
g.async = true;
g.defer = true;
g.setAttribute("src", u + "matomo.js");
g.setAttribute("id", "matomoScript");
headElement.appendChild(g);
}

0 comments on commit 9f3e624

Please sign in to comment.