Skip to content

Commit 60e9d70

Browse files
committed
examples: web analytics updates
* Fix custom report builder: white list measures and dimensions * Add measures dropdown to the audience report
1 parent e0d4e55 commit 60e9d70

File tree

9 files changed

+106
-44
lines changed

9 files changed

+106
-44
lines changed

examples/web-analytics/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Modular and hackable open-source web analytics platform.
1+
Modular and hackable open source web analytics platform.
22

33
## How is it Different from Google Analytics or Matomo?
44

@@ -197,6 +197,7 @@ To obtain `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` you must register an app
197197

198198
* Support multiple tracking applications with [Cube.js multitenancy](https://cube.dev/docs/multitenancy-setup).
199199
* Support geo dimensions and map chart.
200+
* Add filters to custom report builder.
200201

201202
## Contributing
202203

examples/web-analytics/dashboard-app/src/components/DataTable.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from "react";
22
import ChartRenderer from "./ChartRenderer";
33

44
const DataTable = ({ query }) => (
5-
<ChartRenderer height={300} vizState={{ query, chartType: 'table' }} />
5+
<ChartRenderer height={300} vizState={query} />
66
);
77

88
export default DataTable;

examples/web-analytics/dashboard-app/src/pages/AudiencePage.js

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import React from "react";
1+
import React, { useState } from "react";
22
import Grid from "@material-ui/core/Grid";
33

44
import ChartRenderer from "../components/ChartRenderer";
55
import DashboardItem from "../components/DashboardItem";
66
import OverTimeChart from "../components/OverTimeChart";
77
import Chart from "../components/Chart";
8+
import Dropdown from "../components/Dropdown";
89
import SwitchTable from "../components/SwitchTable";
910

1011
const queries = {
@@ -14,7 +15,6 @@ const queries = {
1415
query: {
1516
measures: ['SessionUsers.usersCount'],
1617
timeDimensions: [{
17-
dimension: 'SessionUsers.sessionStart',
1818
granularity: 'day'
1919
}]
2020
}
@@ -83,14 +83,30 @@ const queries = {
8383
}
8484
};
8585

86+
const measuresForSwitch = {
87+
"Users": "SessionUsers.usersCount",
88+
"Sessions": "Sessions.count",
89+
"New Users": "SessionUsers.newUsersCount"
90+
};
8691

8792
const AudiencePage = ({ withTime }) => {
93+
const [overTimeMeasure, setOverTimeMeasure] = useState("Users");
8894
return (
8995
<>
9096
<Grid item xs={12}>
9197
<OverTimeChart
92-
title="Users Over Time"
93-
vizState={withTime(queries.usersOvertime)}
98+
title={
99+
<Dropdown
100+
value={overTimeMeasure}
101+
options={
102+
Object.keys(measuresForSwitch).reduce((out, measure) => {
103+
out[measure] = () => setOverTimeMeasure(measure)
104+
return out;
105+
}, {})
106+
}
107+
/>
108+
}
109+
vizState={withTime({ ...queries.usersOvertime, query: { ...queries.usersOvertime.query, measures: [measuresForSwitch[overTimeMeasure]] } })}
94110
/>
95111
</Grid>
96112
<Grid item xs={6}>

examples/web-analytics/dashboard-app/src/pages/CustomReportPage.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const CustomReportPage = ({ withTime }) => {
3232
};
3333
const dataTableQuery = {
3434
...query,
35+
measures: measures,
3536
timeDimensions: [{
3637
dimension: query.timeDimensions[0].dimension,
3738
}]

examples/web-analytics/dashboard-app/src/pages/CustomReportsBuilderPage.js

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,27 @@ import {
2020
UPDATE_CUSTOM_REPORT
2121
} from "../graphql/mutations";
2222

23+
const allowedMembers = [
24+
"SessionUsers.usersCount",
25+
"SessionUsers.newUsersCount",
26+
"SessionUsers.count",
27+
"SessionUsers.bounceRate",
28+
"SessionUsers.bouncedCount",
29+
"SessionUsers.sessionsPerUser",
2330

31+
"SessionUsers.landingPage",
32+
"SessionUsers.referrerMedium",
33+
"SessionUsers.referrerSource",
34+
"SessionUsers.sourceMedium",
35+
"SessionUsers.type"
36+
];
37+
38+
const whiteListMembers = (members, type, query) => {
39+
return members.filter((member) => {
40+
return allowedMembers.indexOf(member.name) !== -1
41+
&& (query[type] || []).indexOf(member.name) === -1;
42+
})
43+
};
2444

2545
const useStyles = makeStyles(theme => ({
2646
formControl: {
@@ -135,14 +155,14 @@ const CustomReportsBuilderPage = ({ cubejsApi, history }) => {
135155
<MemberSelect
136156
onSelect={updateMeasures.update}
137157
member={measure}
138-
availableMembers={availableMeasures}
158+
availableMembers={whiteListMembers(availableMeasures, 'measures', query)}
139159
onRemove={updateMeasures.remove}
140160
/>
141161
)}
142162
<MemberSelect
143163
title="metric"
144164
onSelect={updateMeasures.add}
145-
availableMembers={availableMeasures}
165+
availableMembers={whiteListMembers(availableMeasures, 'measures', query)}
146166
/>
147167
</FormControl>
148168
<FormControl component="fieldset" className={classes.formControl}>
@@ -157,14 +177,14 @@ const CustomReportsBuilderPage = ({ cubejsApi, history }) => {
157177
<MemberSelect
158178
onSelect={updateDimensions.update}
159179
member={dimension}
160-
availableMembers={availableDimensions}
180+
availableMembers={whiteListMembers(availableDimensions, 'dimensions', query)}
161181
onRemove={updateDimensions.remove}
162182
/>
163183
)}
164184
<MemberSelect
165185
title="dimension"
166186
onSelect={updateDimensions.add}
167-
availableMembers={availableDimensions}
187+
availableMembers={whiteListMembers(availableDimensions, 'dimensions', query)}
168188
/>
169189
</FormControl>
170190
<div>

examples/web-analytics/dashboard-app/src/pages/ReportPage.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ const getDateRange = () => {
3535

3636
const withTimeFunc = ({ query, ...vizState }, begin, end, segment) => {
3737
const timeDimensionObj = (query.timeDimensions || [])[0] || {};
38-
const timeDimension = timeDimensionObj.dimension || 'Sessions.sessionStart';
38+
const cube = (query.measures && query.measures[0].split(".")[0]) || "Sessions";
39+
const timeDimension = timeDimensionObj.dimension || `${cube}.sessionStart`;
3940
const granularity = timeDimensionObj.granularity || null;
4041
const segmentCube = (query) => {
4142
const measureCube = query.measures[0].split(".")[0];

examples/web-analytics/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"dependencies": {
1010
"@cubejs-backend/athena-driver": "^0.18.2",
1111
"@cubejs-backend/mysql-driver": "^0.18.15",
12-
"@cubejs-backend/server": "^0.18.14",
12+
"@cubejs-backend/server": "^0.18.17",
1313
"express-session": "^1.17.0",
1414
"passport": "^0.4.1",
1515
"passport-google-oauth20": "^2.0.0"

examples/web-analytics/schema/Sessions.js

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -44,20 +44,23 @@ cube(`Sessions`, {
4444

4545
usersCount: {
4646
type: `countDistinct`,
47-
sql: `domain_userid`
47+
sql: `domain_userid`,
48+
title: "Users"
4849
},
4950

5051
newUsersCount: {
5152
type: `countDistinct`,
5253
sql: `domain_userid`,
5354
filters: [
5455
{ sql: `${type} = 'New'` }
55-
]
56+
],
57+
title: "New Users"
5658
},
5759

5860
sessionsPerUser: {
5961
sql: `1.000 * ${count} / NULLIF(${usersCount}, 0)`,
60-
type: `number`
62+
type: `number`,
63+
title: `Number of Sessions per User`
6164
},
6265

6366
// Engagement
@@ -66,13 +69,15 @@ cube(`Sessions`, {
6669
type: `count`,
6770
filters:[{
6871
sql: `${isBounced} = 'True'`
69-
}]
72+
}],
73+
title: `Bounces`
7074
},
7175

7276
bounceRate: {
7377
sql: `100.00 * ${bouncedCount} / NULLIF(${count}, 0)`,
7478
type: `number`,
75-
format: `percent`
79+
format: `percent`,
80+
title: `Bounce Rate`
7681
},
7782

7883
averageDurationSeconds: {
@@ -118,10 +123,10 @@ cube(`Sessions`, {
118123
case: {
119124
when: [{ sql: `${CUBE.sessionIndex} = 1`, label: `New`}],
120125
else: { label: `Returning` }
121-
}
126+
},
127+
title: `User Type`
122128
},
123129

124-
125130
// Audience
126131
// Demographics
127132
language: {
@@ -159,14 +164,22 @@ cube(`Sessions`, {
159164
}
160165
},
161166

167+
// Acquisition
168+
landingPage: {
169+
type: `string`,
170+
sql: `page_url_path`,
171+
title: `Landing Page`
172+
},
173+
162174
referrerMedium: {
163175
type: `string`,
164176
case: {
165177
when: [
166178
{ sql: `${CUBE}.referrer_medium != ''`, label: { sql: `${CUBE}.referrer_medium` } }
167179
],
168180
else: { label: '(none)' }
169-
}
181+
},
182+
title: `Medium`
170183
},
171184

172185
referrerSource: {
@@ -176,12 +189,14 @@ cube(`Sessions`, {
176189
{ sql: `${CUBE}.referrer_source != ''`, label: { sql: `${CUBE}.referrer_source` } }
177190
],
178191
else: { label: '(none)' }
179-
}
192+
},
193+
title: `Source`
180194
},
181195

182196
sourceMedium: {
183197
type: `string`,
184-
sql: `concat(${CUBE.referrerSource}, " / ", ${CUBE.referrerMedium})`
198+
sql: `concat(${CUBE.referrerSource}, " / ", ${CUBE.referrerMedium})`,
199+
title: `Source / Medium`
185200
}
186201
},
187202

@@ -206,23 +221,21 @@ cube(`Sessions`, {
206221
refreshKey: {
207222
every: `5 minutes`
208223
},
209-
external: true
224+
external: true,
225+
scheduledRefresh: true
210226
},
211227
additive: {
212228
type: `rollup`,
213229
measureReferences: [totalDuration, bouncedCount, count],
214230
segmentReferences: [bouncedSessions, directTraffic, searchTraffic, newUsers],
231+
dimensionReferences: [landingPage],
215232
timeDimensionReference: sessionStart,
216233
granularity: `hour`,
217234
refreshKey: {
218235
every: `5 minutes`
219236
},
220-
indexes: {
221-
bouncedSessions: {
222-
columns: [bouncedSessions]
223-
}
224-
},
225-
external: true
237+
external: true,
238+
scheduledRefresh: true
226239
}
227240
}
228241
});
@@ -237,6 +250,7 @@ cube(`SessionUsers`, {
237250
domain_userid,
238251
session_index,
239252
br_lang,
253+
page_url_path,
240254
geo_country,
241255
geo_city,
242256
referrer_source,

examples/web-analytics/yarn.lock

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,15 @@
811811
ramda "^0.27.0"
812812
redis "^2.8.0"
813813

814+
"@cubejs-backend/query-orchestrator@^0.18.17":
815+
version "0.18.17"
816+
resolved "https://registry.yarnpkg.com/@cubejs-backend/query-orchestrator/-/query-orchestrator-0.18.17.tgz#3a997f43d1e29b373d08905afb6eaec3afc83c68"
817+
integrity sha512-3T+pbSwzNazZEXR3remoexyi1/E+wp1iu1Ffvz39LgbcoUt1wJEHt5pLnv0/LOMo37qnQqsFkJQrR6rAO0srlw==
818+
dependencies:
819+
generic-pool "^3.7.1"
820+
ramda "^0.27.0"
821+
redis "^2.8.0"
822+
814823
"@cubejs-backend/query-orchestrator@^0.18.2":
815824
version "0.18.2"
816825
resolved "https://registry.yarnpkg.com/@cubejs-backend/query-orchestrator/-/query-orchestrator-0.18.2.tgz#b9256e2d961e4feb5ff321e7022ed64b76658258"
@@ -820,10 +829,10 @@
820829
ramda "^0.27.0"
821830
redis "^2.8.0"
822831

823-
"@cubejs-backend/schema-compiler@^0.18.14":
824-
version "0.18.14"
825-
resolved "https://registry.yarnpkg.com/@cubejs-backend/schema-compiler/-/schema-compiler-0.18.14.tgz#0cc4e9e81ac661e895f5d69d69a139775c33bf57"
826-
integrity sha512-eJ4gSbjXUHOGbUpVrx4tQftBrHwicdnsehUzKUDMwtybezMhTxdWhGHPDDmHM1D0TS/4sbAMropDzkU4f3U4SQ==
832+
"@cubejs-backend/schema-compiler@^0.18.17":
833+
version "0.18.17"
834+
resolved "https://registry.yarnpkg.com/@cubejs-backend/schema-compiler/-/schema-compiler-0.18.17.tgz#b7d59c8624c9348d9d60abf0cf904347f1af2e4b"
835+
integrity sha512-OcHFnsKVksQJyVfKBrCFBFuXd/Oc+mlEZ0Bubr9wmy0MlBsrtaagkzJrg4lJkg61zhbxNvdjMIvGWoohKpDgTw==
827836
dependencies:
828837
"@babel/generator" "^7.4.0"
829838
"@babel/parser" "^7.4.2"
@@ -840,10 +849,10 @@
840849
ramda "^0.27.0"
841850
syntax-error "^1.3.0"
842851

843-
"@cubejs-backend/server-core@^0.18.14":
844-
version "0.18.14"
845-
resolved "https://registry.yarnpkg.com/@cubejs-backend/server-core/-/server-core-0.18.14.tgz#34c6369b4fdd56c884c5fd0fee196203589e6a7b"
846-
integrity sha512-EFDfLU6lZzytjXasNCAq6bGg80WnJ36XjpogAoIqViMQXYoF03ULsTlOXccbl/IJi1WPajsaSV2nQTG+lOK1Xg==
852+
"@cubejs-backend/server-core@^0.18.17":
853+
version "0.18.17"
854+
resolved "https://registry.yarnpkg.com/@cubejs-backend/server-core/-/server-core-0.18.17.tgz#881c941f4cabe8893d16ba0041d2ccb2a0826822"
855+
integrity sha512-q5G7ulqlhO7cMORLpuTPW+j2w5YY8l3MoyU05TkQufdP4c9Qatb1lUwRpfpgGwlBb71gC/dEjm/WHrNFf+jsxg==
847856
dependencies:
848857
"@babel/core" "^7.7.4"
849858
"@babel/generator" "^7.4.0"
@@ -854,8 +863,8 @@
854863
"@babel/traverse" "^7.4.0"
855864
"@babel/types" "^7.4.0"
856865
"@cubejs-backend/api-gateway" "^0.18.7"
857-
"@cubejs-backend/query-orchestrator" "^0.18.13"
858-
"@cubejs-backend/schema-compiler" "^0.18.14"
866+
"@cubejs-backend/query-orchestrator" "^0.18.17"
867+
"@cubejs-backend/schema-compiler" "^0.18.17"
859868
codesandbox-import-utils "^2.1.12"
860869
cross-spawn "^7.0.1"
861870
fs-extra "^8.1.0"
@@ -871,12 +880,12 @@
871880
sqlstring "^2.3.1"
872881
uuid "^3.3.3"
873882

874-
"@cubejs-backend/server@^0.18.14":
875-
version "0.18.14"
876-
resolved "https://registry.yarnpkg.com/@cubejs-backend/server/-/server-0.18.14.tgz#1f14ad1def42f3bb30f0ce808b62925246adbb9f"
877-
integrity sha512-tq8B5e/mNO9nG9MoOGH2Tn/mlOeU9TjBHPhY862RjL7o1dVVeUK4EWtoMkuUyCrfyXY8ZhOmJMLgylXkeOl/Ag==
883+
"@cubejs-backend/server@^0.18.17":
884+
version "0.18.17"
885+
resolved "https://registry.yarnpkg.com/@cubejs-backend/server/-/server-0.18.17.tgz#1b1739da567f74634d9d40a36d9326b53dce9619"
886+
integrity sha512-HergCBh3SSB49+1aR29oa9NKlOm+p7uzkdZEAzaP/AUYQRKKEC0JeeZYZVG6uC9XFCxKAiYf3vMaCbVLilYUFw==
878887
dependencies:
879-
"@cubejs-backend/server-core" "^0.18.14"
888+
"@cubejs-backend/server-core" "^0.18.17"
880889
body-parser "^1.15.2"
881890
codesandbox-import-utils "^2.1.12"
882891
cors "^2.8.4"

0 commit comments

Comments
 (0)