UNPKG

8.9 kBJavaScriptView Raw
1/*
2 * Workhorse module for Google Analytics interaction.
3 */
4
5var googleapis = require('googleapis'),
6 ga = googleapis.analytics('v3'),
7 fs = require('fs'),
8 path = require('path');
9
10var config = require('./config');
11
12var jwt = new googleapis.auth.JWT(
13 config.email,
14 config.key,
15 null,
16 ['https://www.googleapis.com/auth/analytics.readonly']
17);
18
19
20// The reports we want to run.
21var reports_path = path.join(__dirname, "reports.json");
22var reports = JSON.parse(fs.readFileSync(reports_path)).reports;
23var by_name = {};
24for (var i=0; i<reports.length; i++)
25 by_name[reports[i].name] = reports[i];
26
27// Google Analytics data fetching and transformation utilities.
28// This should really move to its own analytics.js file.
29var Analytics = {
30
31 reports: by_name,
32
33 query: function(report, callback) {
34
35 // Insert IDs and auth data. Dupe the object so it doesn't
36 // modify the report object for later work.
37 var query = {};
38
39 if (report.query.dimensions)
40 query.dimensions = report.query.dimensions.join(",");
41
42 if (report.query.metrics)
43 query.metrics = report.query.metrics.join(",");
44
45 if (report.query['start-date'])
46 query["start-date"] = report.query['start-date'];
47 if (report.query['end-date'])
48 query["end-date"] = report.query['end-date'];
49
50
51 // Optional filters.
52 if (report.query.filters)
53 query.filters = report.query.filters.join(",");
54
55 query['max-results'] = report.query['max-results'] || 10000;
56
57 if (report.query['sort'])
58 query['sort'] = report.query['sort'];
59
60
61 // Specify the account, and auth token.
62 query.ids = config.account.ids;
63 query.auth = jwt;
64
65 var api_call;
66 if (report.frequency == "realtime")
67 api_call = ga.data.realtime.get;
68 else
69 api_call = ga.data.ga.get;
70
71 jwt.authorize(function(err, result) {
72 if (err) return callback(err, null);
73
74 api_call(query, function(err, result) {
75 if (err) return callback(err, null);
76
77 if (config.debug)
78 fs.writeFileSync("data/google/" + report.name + ".json", JSON.stringify(result, null, 2));
79
80 callback(null, Analytics.process(report, result));
81 });
82 });
83 },
84
85 // translate 20141228 -> 2014-12-28
86 date_format: function(in_date) {
87 return [in_date.substr(0,4), in_date.substr(4, 2), in_date.substr(6, 2)].join("-")
88 },
89
90 mapping: {
91 "ga:date": "date",
92 "ga:hour": "hour",
93 "ga:users": "visitors",
94 "rt:activeUsers": "active_visitors",
95 "rt:pagePath": "page",
96 "rt:pageTitle": "page_title",
97 "ga:sessions": "visits",
98 "ga:deviceCategory": "device",
99 "ga:operatingSystem": "os",
100 "ga:operatingSystemVersion": "os_version",
101 "ga:hostname": "domain",
102 "ga:browser" : 'browser',
103 "ga:browserVersion" : "browser_version",
104 "ga:source": "source"
105 },
106
107 // The OSes we care about for the OS breakdown. The rest can be "Other".
108 // These are the extract strings used by Google Analytics.
109 oses: [
110 "Android", "BlackBerry", "Windows Phone", "iOS",
111 "Linux", "Macintosh", "Windows"
112 ],
113
114 // The versions of Windows we care about for the Windows version breakdown.
115 // The rest can be "Other". These are the exact strings used by Google Analytics.
116 windows_versions: [
117 "XP", "Vista", "7", "8", "8.1"
118 ],
119
120 // The browsers we care about for the browser report. The rest are "Other"
121 // These are the exact strings used by Google Analytics.
122 browsers: [
123 "Internet Explorer", "Chrome", "Safari", "Firefox", "Android Browser",
124 "Safari (in-app)", "Amazon Silk", "Opera", "Opera Mini",
125 "IE with Chrome Frame", "BlackBerry", "UC Browser"
126 ],
127
128 // The versions of IE we care about for the IE version breakdown.
129 // The rest can be "Other". These are the exact strings used by Google Analytics.
130 ie_versions: [
131 "11.0", "10.0", "9.0", "8.0", "7.0", "6.0"
132 ],
133
134
135 // Given a report and a raw google response, transform it into our schema.
136 process: function(report, data) {
137 var result = {
138 name: report.name,
139 query: report.query,
140 meta: report.meta,
141 data: [],
142 totals: {}
143 };
144
145 // Calculate each individual data point.
146 for (var i=0; i<data.rows.length; i++) {
147 var row = data.rows[i];
148
149 var point = {};
150 for (var j=0; j<row.length; j++) {
151 var field = Analytics.mapping[data.columnHeaders[j].name];
152 var value = row[j];
153
154 if (field == "date")
155 value = Analytics.date_format(value);
156
157 point[field] = value;
158 }
159
160 result.data.push(point);
161 }
162
163 // Go through those data points to calculate totals.
164 // Right now, this is totally report-specific.
165 if ("visitors" in result.data[0]) {
166 result.totals.visitors = 0;
167 for (var i=0; i<result.data.length; i++)
168 result.totals.visitors += parseInt(result.data[i].visitors);
169 }
170 if ("sessions" in result.data[0]) {
171 result.totals.sessions = 0;
172 for (var i=0; i<result.data.length; i++)
173 result.totals.sessions += parseInt(result.data[i].sessions);
174 }
175
176 if (report.name == "devices") {
177 result.totals.devices = {mobile: 0, desktop: 0, tablet: 0};
178 for (var i=0; i<result.data.length; i++)
179 result.totals.devices[result.data[i].device] += parseInt(result.data[i].visits);
180 }
181
182 if (report.name == "os") {
183 // initialize all cared-about OSes to 0
184 result.totals.os = {};
185 for (var i=0; i<Analytics.oses.length; i++)
186 result.totals.os[Analytics.oses[i]] = 0;
187 result.totals.os["Other"] = 0;
188
189 for (var i=0; i<result.data.length; i++) {
190 var os = result.data[i].os;
191
192 // Bucket any we don't care about under "Other".
193 if (Analytics.oses.indexOf(os) < 0)
194 os = "Other";
195
196 result.totals.os[os] += parseInt(result.data[i].visits);
197 }
198 }
199
200 if (report.name == "windows") {
201 // initialize all cared-about versions to 0
202 result.totals.os_version = {};
203 for (var i=0; i<Analytics.windows_versions.length; i++)
204 result.totals.os_version[Analytics.windows_versions[i]] = 0;
205 result.totals.os_version["Other"] = 0;
206
207 for (var i=0; i<result.data.length; i++) {
208 var version = result.data[i].os_version;
209
210 // Bucket any we don't care about under "Other".
211 if (Analytics.windows_versions.indexOf(version) < 0)
212 version = "Other";
213
214 result.totals.os_version[version] += parseInt(result.data[i].visits);
215 }
216 }
217
218 if (report.name == "browsers") {
219
220 result.totals.browser = {};
221 for (var i=0; i<Analytics.browsers.length; i++)
222 result.totals.browser[Analytics.browsers[i]] = 0;
223 result.totals.browser["Other"] = 0;
224
225 for (var i=0; i<result.data.length; i++) {
226 var browser = result.data[i].browser;
227
228 if (Analytics.browsers.indexOf(browser) < 0)
229 browser = "Other";
230
231 result.totals.browser[browser] += parseInt(result.data[i].visits);
232 }
233 }
234
235 if (report.name == "ie") {
236 // initialize all cared-about versions to 0
237 result.totals.ie_version = {};
238 for (var i=0; i<Analytics.ie_versions.length; i++)
239 result.totals.ie_version[Analytics.ie_versions[i]] = 0;
240 result.totals.ie_version["Other"] = 0;
241
242 for (var i=0; i<result.data.length; i++) {
243 var version = result.data[i].browser_version;
244
245 // Bucket any we don't care about under "Other".
246 if (Analytics.ie_versions.indexOf(version) < 0)
247 version = "Other";
248
249 result.totals.ie_version[version] += parseInt(result.data[i].visits);
250 }
251 }
252
253 // presumably we're organizing these by date
254 if (result.data[0].date) {
255 result.totals.start_date = result.data[0].date;
256 result.totals.end_date = result.data[result.data.length-1].date;
257 }
258
259 // datestamp all reports, will be serialized in JSON as ISO 8601
260 result.taken_at = new Date();
261
262 return result;
263 }
264
265};
266
267module.exports = Analytics;