diff --git a/.gitignore b/.gitignore
index cecfe5d..99e039a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/docs/documentation/usage-data.md b/docs/documentation/usage-data.md
new file mode 100644
index 0000000..0529fd4
--- /dev/null
+++ b/docs/documentation/usage-data.md
@@ -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.
diff --git a/docs/views/tutorials-and-examples.html b/docs/views/tutorials-and-examples.html
index d98b308..0214b4e 100644
--- a/docs/views/tutorials-and-examples.html
+++ b/docs/views/tutorials-and-examples.html
@@ -58,8 +58,11 @@
Getting started
-
Basic usage
+
Getting started
-
Advanced usage
+
Advanced topics
-
Passing data from page to page
diff --git a/lib/usage-data-prompt.txt b/lib/usage-data-prompt.txt
new file mode 100644
index 0000000..9002331
--- /dev/null
+++ b/lib/usage-data-prompt.txt
@@ -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)
diff --git a/lib/usage_data.js b/lib/usage_data.js
new file mode 100644
index 0000000..898e765
--- /dev/null
+++ b/lib/usage_data.js
@@ -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()
+}
diff --git a/start.js b/start.js
index 94dd0fa..e3812fc 100644
--- a/start.js
+++ b/start.js
@@ -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')
+
+// Get usageDataConfig from file, if exists
+const usageDataConfig = usageData.getUsageDataConfig()
+
+if (usageDataConfig.collectUsageData === undefined) {
+ // didn't opted
+ checkFiles()
+ 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
+ checkFiles()
+ runGulp()
+ usageData.startTracking(usageDataConfig)
+} else {
+ // opted out
+ checkFiles()
+ 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())
+ })
+}