Skip to content
This repository has been archived by the owner on Jul 17, 2018. It is now read-only.

Commit

Permalink
Setup usage tracking and add documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
joelanman authored and alex-ju committed Mar 28, 2018
1 parent 6b2711d commit 891405d
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 23 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# ignore tracking usage data config - we want a different one for each user
usage-data-config.json
.env
.sass-cache
.DS_Store
Expand Down
27 changes: 27 additions & 0 deletions docs/documentation/usage-data.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Collecting usage data

You can choose to have the Prototype Kit send anonymous usage data for analysis.
This helps the team working on the Kit understand how it's being used, in order
to improve it - please don't turn off usage data unless you have to.

## How it works

When you first run the Prototype Kit, it will ask you for permission to send
usage data to Google Analytics. It will store your answer in `usage-data-config.json` and it won't ask
you again.

If you say yes, it will store an anonymous, unique ID number in `usage-data-config.json`.

## Data we collect

Whenever you start the Prototype Kit, it will send:

- your anonymous ID number
- the Prototype Kit version number
- your operating system (for example 'Windows 10')
- your Node.js version

## Change usage data settings

You can start or stop sending usage data at any time. Delete `usage-data-config.json`
and restart the Prototype Kit. It will ask you again whether you'd like to send data.
7 changes: 5 additions & 2 deletions docs/views/tutorials-and-examples.html
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,11 @@ <h3 class="govuk-heading-m">Getting started</h3>

</div>
<div class="govuk-o-grid__item govuk-o-grid__item--one-third govuk-!-mt-r6">
<h3 class="govuk-heading-m">Basic usage</h3>
<h3 class="govuk-heading-m">Getting started</h3>
<ul class="govuk-list govuk-list--bullet">
<li>
<a href="/docs/usage-data">Sending kit usage data</a>
</li>
<li>
<a href="/docs/making-pages">Making pages</a>
</li>
Expand All @@ -84,7 +87,7 @@ <h3 class="govuk-heading-m">Basic usage</h3>
</ul>
</div>
<div class="govuk-o-grid__item govuk-o-grid__item--one-third govuk-!-mt-r6">
<h3 class="govuk-heading-m">Advanced usage</h3>
<h3 class="govuk-heading-m">Advanced topics</h3>
<ul class="govuk-list govuk-list--bullet">
<li>
<a href="/docs/examples/pass-data">Passing data from page to page</a>
Expand Down
9 changes: 9 additions & 0 deletions lib/usage-data-prompt.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

Help us improve the GOV.UK Prototype Kit
────────────────────────────────────────

With your permission, the kit can send useful anonymous usage data
for analysis to help the team improve the service. Read more here:
https://govuk-prototype-kit-beta.herokuapp.com/docs/usage-data

Do you give permission for the kit to send anonymous usage data? (y/n)
95 changes: 95 additions & 0 deletions lib/usage_data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Core dependencies
const path = require('path')
const fs = require('fs')
const os = require('os')

// NPM dependencies
const prompt = require('prompt')
const universalAnalytics = require('universal-analytics')
const uuidv4 = require('uuid/v4')

// Local dependencies
const packageJson = require('../package.json')

exports.getUsageDataConfig = function () {
// Try to read config file to see if usage data is opted in
let usageDataConfig = {}
try {
usageDataConfig = require(path.join(__dirname, '../usage-data-config.json'))
} catch (e) {
// do nothing - we will make a config
}
return usageDataConfig
}

exports.setUsageDataConfig = function (usageDataConfig) {
const usageDataConfigJSON = JSON.stringify(usageDataConfig, null, ' ')
try {
fs.writeFileSync(path.join(__dirname, '../usage-data-config.json'), usageDataConfigJSON)
return true
} catch (error) {
console.error(error)
}
return false
}

// Ask for permission to track data
// returns a Promise with the user's answer
exports.askForUsageDataPermission = function () {
return new Promise(function (resolve, reject) {
// Set up prompt settings
prompt.colors = false
prompt.start()
prompt.message = ''
prompt.delimiter = ''

const description = fs.readFileSync(path.join(__dirname, 'usage-data-prompt.txt'), 'utf8')

prompt.get([{
name: 'answer',
description: description,
required: true,
type: 'string',
pattern: /y(es)?|no?/i,
message: 'Please enter y or n'
}], function (err, result) {
if (err) {
reject(err)
}
if (result.answer.match(/y(es)?/i)) {
resolve('yes')
} else {
resolve('no')
}
})
})
}

exports.startTracking = function (usageDataConfig) {
// Get client ID for tracking
// https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#cid

if (usageDataConfig.clientId === undefined) {
usageDataConfig.clientId = uuidv4()
exports.setUsageDataConfig(usageDataConfig)
}

// Track kit start event, with kit version, operating system and Node.js version
const trackingId = 'UA-26179049-21'
const trackingUser = universalAnalytics(trackingId, usageDataConfig.clientId)

const kitVersion = packageJson.version
const operatingSystem = os.platform() + ' ' + os.release()
const nodeVersion = process.versions.node

// Anonymise the IP
trackingUser.set('anonymizeIp', 1)

// Set custom dimensions
trackingUser.set('cd1', operatingSystem)
trackingUser.set('cd2', kitVersion)
trackingUser.set('cd3', nodeVersion)

// Trigger start event
trackingUser.event('State', 'Start').send()
}
80 changes: 59 additions & 21 deletions start.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,69 @@
const path = require('path')
const fs = require('fs')

// Warn if node_modules folder doesn't exist
// HACK: Check /express since we are committing in node_modules in this prototype version of the kit
const nodeModulesExists = fs.existsSync(path.join(__dirname, '/node_modules', '/express'))
if (!nodeModulesExists) {
console.error('ERROR: Node module folder missing. Try running `npm install`')
process.exit(0)
// Local dependencies
const usageData = require('./lib/usage_data')

// Check /express since we are committing in node_modules in this prototype version of the kit
checkFiles()

// Get usageDataConfig from file, if exists
const usageDataConfig = usageData.getUsageDataConfig()

if (usageDataConfig.collectUsageData === undefined) {
// No recorded answer, so ask for permission
let promptPromise = usageData.askForUsageDataPermission()
promptPromise.then(function (answer) {
if (answer === 'yes') {
usageDataConfig.collectUsageData = true
usageData.setUsageDataConfig(usageDataConfig)
usageData.startTracking(usageDataConfig)
} else if (answer === 'no') {
usageDataConfig.collectUsageData = false
usageData.setUsageDataConfig(usageDataConfig)
} else {
console.error(answer)
}
runGulp()
})
} else if (usageDataConfig.collectUsageData === true) {
// Opted in
usageData.startTracking(usageDataConfig)
runGulp()
} else {
// Opted out
runGulp()
}

// Create template .env file if it doesn't exist
const envExists = fs.existsSync(path.join(__dirname, '/.env'))
if (!envExists) {
console.log('Creating template .env file')
fs.createReadStream(path.join(__dirname, '/lib/template.env'))
.pipe(fs.createWriteStream(path.join(__dirname, '/.env')))
function checkFiles () {
// Warn if node_modules folder doesn't exist
// HACK: Check /express since we are committing in node_modules in this prototype version of the kit
const nodeModulesExists = fs.existsSync(path.join(__dirname, '/node_modules', '/express'))
if (!nodeModulesExists) {
console.error('ERROR: Node module folder missing. Try running `npm install`')
process.exit(0)
}

// Create template .env file if it doesn't exist
const envExists = fs.existsSync(path.join(__dirname, '/.env'))
if (!envExists) {
console.log('Creating template .env file')
fs.createReadStream(path.join(__dirname, '/lib/template.env'))
.pipe(fs.createWriteStream(path.join(__dirname, '/.env')))
}
}

// Run gulp
const spawn = require('cross-spawn')
function runGulp () {
const spawn = require('cross-spawn')

process.env['FORCE_COLOR'] = 1
var gulp = spawn('gulp')
gulp.stdout.pipe(process.stdout)
gulp.stderr.pipe(process.stderr)
process.stdin.pipe(gulp.stdin)
process.env['FORCE_COLOR'] = 1
var gulp = spawn('gulp')
gulp.stdout.pipe(process.stdout)
gulp.stderr.pipe(process.stderr)
process.stdin.pipe(gulp.stdin)

gulp.on('exit', function (code) {
console.log('gulp exited with code ' + code.toString())
})
gulp.on('exit', function (code) {
console.log('gulp exited with code ' + code.toString())
})
}

0 comments on commit 891405d

Please sign in to comment.