Skip to content

Commit 773f5ad

Browse files
bodom0015lmarini
andauthored
Add support for Amplitude clickstream/tracking (#132)
* Allow admin/customize to specify an amplitude access key * If configured, include Amplitude tracking snippet on each view * fix: slimmed-down basic Amplitude event tracking * Count views of files/datasets/collections by id * Count file/dataset submissions to each extractor * Count new file uploads to datasets (via the UI) * Capture viewer details for view_resource events * Capture submitter details for extraction events * Added CHANGELOG entry Co-authored-by: Luigi Marini <[email protected]>
1 parent 3660e3b commit 773f5ad

File tree

12 files changed

+155
-6
lines changed

12 files changed

+155
-6
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/)
55
and this project adheres to [Semantic Versioning](http://semver.org/).
66

7+
## Unreleased
8+
9+
### Added
10+
- Added support for Amplitude clickstream tracking. See Admin -> Customize to configure Amplitude apikey.
11+
12+
713
## 1.12.1 - 2020-11-05
814

915
### Fixed

app/api/Admin.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ class Admin @Inject() (userService: UserService,
8080
getValueString(request.body, "sensor").foreach(AppConfiguration.setSensorTitle(_))
8181
getValueString(request.body, "parameters").foreach(AppConfiguration.setParametersTitle(_))
8282
getValueString(request.body, "parameter").foreach(AppConfiguration.setParameterTitle(_))
83+
getValueString(request.body, "amplitudeApikey").foreach(AppConfiguration.setAmplitudeApiKey(_))
84+
8385
getValueString(request.body, "tosText").foreach { tos =>
8486
events.addEvent(Event(request.user.get, event_type = EventType.TOS_UPDATE.toString))
8587
AppConfiguration.setTermsOfServicesText(tos)

app/controllers/Admin.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ class Admin @Inject() (sectionIndexInfo: SectionIndexInfoService, userService: U
2929
Ok(views.html.admin.customize(theme,
3030
AppConfiguration.getDisplayName,
3131
AppConfiguration.getWelcomeMessage,
32-
AppConfiguration.getGoogleAnalytics))
32+
AppConfiguration.getGoogleAnalytics,
33+
AppConfiguration.getAmplitudeApiKey))
3334
}
3435

3536
def tos = ServerAdminAction { implicit request =>

app/services/AppConfigurationService.scala

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,23 @@ object AppConfiguration {
116116
/** Set the google analytics code */
117117
def setGoogleAnalytics(gacode: String) = appConfig.setProperty("google.analytics", gacode)
118118

119-
/** Get the welcome message */
119+
/** Get the google analytics code */
120120
def getGoogleAnalytics: String = appConfig.getProperty("google.analytics", "")
121121

122122
// ----------------------------------------------------------------------
123123

124+
/** Set the Amplitude clickstream/analytics configuration */
125+
def setAmplitudeApiKey(ampApiKey: String) = {
126+
appConfig.setProperty("amplitude.apikey", ampApiKey)
127+
}
128+
129+
/** Get the Amplitude clickstream/analytics configuration */
130+
def getAmplitudeApiKey: String = {
131+
appConfig.getProperty("amplitude.apikey", "")
132+
}
133+
134+
// ----------------------------------------------------------------------
135+
124136
/** Set the Sensors title */
125137
def setSensorsTitle(sensorsTitle: String) = appConfig.setProperty("sensors.title", sensorsTitle)
126138

app/views/admin/customize.scala.html

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
@(theme: String, displayName: String, welcomeMessage: String, googleAnalytics: String)(implicit user: Option[models.User])
1+
@(theme: String, displayName: String, welcomeMessage: String, googleAnalytics: String, amplitudeApiKey: String)(implicit user: Option[models.User])
22
@main("Customize") {
33
<div class="page-header">
44
<h1>Customize</h1>
@@ -49,7 +49,16 @@ <h1>Customize</h1>
4949
<div class="col-md-6">
5050
<div class="form-group">
5151
<label for="googleAnalytics">Google Analytics Code</label>
52-
<input class="form-control" id="googleAnalytics" name="googleAnalytics" type="text" value="@(googleAnalytics)"></input>
52+
<input class="form-control" id="googleAnalytics" name="googleAnalytics" type="text" value="@(googleAnalytics)" />
53+
</div>
54+
</div>
55+
</div>
56+
57+
<div class="row">
58+
<div class="col-md-6">
59+
<div class="form-group">
60+
<label for="amplitudeApiKey">Amplitude API Key</label>
61+
<input class="form-control" id="amplitudeApiKey" name="amplitudeApiKey" type="text" value="@(amplitudeApiKey)" />
5362
</div>
5463
</div>
5564
</div>
@@ -101,13 +110,16 @@ <h1>Customize</h1>
101110
var theme = $("#themeSelect").val().trim();
102111
var googleAnalytics = $("#googleAnalytics").val().trim();
103112

113+
var amplitudeApiKey = $("#amplitudeApiKey").val().trim();
114+
104115
$.ajax({
105116
url: "@api.routes.Admin.updateConfiguration",
106117
data: JSON.stringify({
107118
displayName: displayName,
108119
welcomeMessage: welcomeMessage,
109120
theme: theme,
110-
googleAnalytics: googleAnalytics
121+
googleAnalytics: googleAnalytics,
122+
amplitudeApikey: amplitudeApiKey
111123
}),
112124
type: "POST",
113125
contentType: "application/json"

app/views/collectionofdatasets.scala.html

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
@import models.UUID
1414
@import api.Permission
1515
@import _root_.util.Formatters._
16+
@import services.AppConfiguration
1617

1718
@showPreview(name: String, url: String, collection_id: String) = {
1819
<script>
@@ -33,7 +34,19 @@
3334
<script src="@routes.Assets.at("javascripts/spaceModify.js")" type="text/javascript"></script>
3435
<script src="@routes.Assets.at("javascripts/htmlEncodeDecode.js")" language="javascript"></script>
3536
<script src="@routes.Assets.at("javascripts/select.js")" type="text/javascript"></script>
36-
37+
@if(AppConfiguration.getAmplitudeApiKey != "") {
38+
<script>
39+
amplitude.getInstance().logEvent("view_resource", {
40+
"type": "collection",
41+
"resource_id": '@collection.id',
42+
"resource_name": '@collection.name',
43+
"author_id": '@collection.author.id',
44+
"author_name": '@collection.author.fullName',
45+
"viewer_id": '@user.get.id',
46+
"viewer_name": '@user.get.getMiniUser.fullName'
47+
});
48+
</script>
49+
}
3750
<div class="row">
3851
<!-- left column -->
3952
<div class="col-md-8">

app/views/dataset.scala.html

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,24 @@
2222
@import api.Permission
2323
@import play.api.Play.current
2424
@import _root_.util.Formatters._
25+
@import services.AppConfiguration
2526

2627
@main(dataset.name) {
2728
<link rel="stylesheet" href="@routes.Assets.at("stylesheets/glyphicon-animations.css")">
2829
<script src="@routes.Assets.at("javascripts/htmlEncodeDecode.js")" language="javascript"></script>
30+
@if(AppConfiguration.getAmplitudeApiKey != "") {
31+
<script>
32+
amplitude.getInstance().logEvent("view_resource", {
33+
"type": "dataset",
34+
"resource_id": '@dataset.id',
35+
"resource_name": '@dataset.name',
36+
"author_id": '@dataset.author.id',
37+
"author_name": '@dataset.author.fullName',
38+
"viewer_id": '@user.get.id',
39+
"viewer_name": '@user.get.getMiniUser.fullName'
40+
});
41+
</script>
42+
}
2943
<script type="text/javascript">
3044
var spaceId = "@currentSpace";
3145
var descLabel = "@Messages("dataset.description")";

app/views/datasets/uploadFiles.scala.html

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
@(dataset: Dataset, folderId: Option[String])(implicit user: Option[models.User])
22
@import play.api.i18n.Messages
3+
@import services.AppConfiguration
34
<!--
45
The sections of this file dealing with the multi file uploader library are loosely based on the demo
56
of the blueimp jQuery File Upload library. An open source project located here: http://blueimp.github.io/jQuery-File-Upload/
@@ -126,6 +127,7 @@ <h3 class="title"></h3>
126127
<!-- The template to display files available for download -->
127128
<script id="template-download" type="text/x-tmpl">
128129
{% for (var i=0, file; file=o.files[i]; i++) { %}
130+
{% logEvent(file.size); %}
129131
<tr class="template-download fade">
130132
{% if (file.deleteUrl) { %}
131133
<td>
@@ -171,6 +173,22 @@ <h3 class="title"></h3>
171173

172174
<!-- The Templates plugin is included to render the upload/download listings - downloaded to make the resource local -->
173175
<script src="@routes.Assets.at("javascripts/file-uploader/tmpl.min.js")"></script>
176+
177+
<script>
178+
// Inject some info into templates for tracking
179+
@if(AppConfiguration.getAmplitudeApiKey != "") {
180+
tmpl.helper += ',logEvent=function(size){' +
181+
'amplitude.logEvent("upload", {' +
182+
'"dataset_id":"@dataset.id",' +
183+
'"dataset_name":"@dataset.name",' +
184+
'"uploader_id":"@user.get.id",' +
185+
'"uploader_name":"@user.get.getMiniUser.fullName",' +
186+
'"length":size,' +
187+
'});}'
188+
} else {
189+
tmpl.helper += ',logEvent=function(){}'
190+
}
191+
</script>
174192
<!-- The Load Image plugin is included for the preview images and image resizing functionality - downloaded to make the resource local -->
175193
<script src="@routes.Assets.at("javascripts/file-uploader/load-image.all.min.js")"></script>
176194
<!-- The Canvas to Blob plugin is included for image resizing functionality - downloaded to make the resource local -->

app/views/extractions/submitDatasetExtraction.scala.html

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
@(extractors: List[ExtractorInfo], ds: Dataset)(implicit user: Option[models.User])
22
@import play.api.libs.json._
3+
@import services.AppConfiguration
34

45
@main("Extractions") {
56
<div class="row">
@@ -70,6 +71,19 @@ <h1>Submit dataset for extraction</h1>
7071
function submit(extractor_name, textbox_id, ds_id, submit_id) {
7172
var clickedBtn = $('#' + submit_id);
7273

74+
@if(AppConfiguration.getAmplitudeApiKey != "") {
75+
amplitude.getInstance().logEvent("extraction", {
76+
"view": "dataset",
77+
"extractor_name": extractor_name,
78+
"resource_id": '@ds.id',
79+
"resource_name": '@ds.name',
80+
"author_id": '@ds.author.id',
81+
"author_name": '@ds.author.fullName',
82+
"submitter_id": '@user.get.id',
83+
"submitter_name": '@user.get.getMiniUser.fullName'
84+
});
85+
}
86+
7387
// Throttle submissions to one every 3 seconds
7488
disableSubmit(clickedBtn);
7589
setTimeout(function() {

app/views/extractions/submitFileExtraction.scala.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
@(extractors: List[ExtractorInfo], file: File, folderHierarchy: List[Folder], spaces:List[ProjectSpace], allDatasets: List[Dataset])(implicit user: Option[models.User])
22
@import _root_.util.Formatters._
33
@import play.api.libs.json._
4+
@import services.AppConfiguration
45

56
@main("Extractions") {
67
<ol class="breadcrumb">
@@ -108,6 +109,18 @@ <h1>Submit file for extraction</h1>
108109

109110
function submit(extractor_name, textbox_id, file_id, submit_id) {
110111
var clickedBtn = $('#' + submit_id);
112+
@if(AppConfiguration.getAmplitudeApiKey != "") {
113+
amplitude.getInstance().logEvent("extraction", {
114+
"view": "file",
115+
"extractor_name": extractor_name,
116+
"resource_id": '@file.id',
117+
"resource_name": '@file.filename',
118+
"author_id": '@file.author.id',
119+
"author_name": '@file.author.fullName',
120+
"submitter_id": '@user.get.id',
121+
"submitter_name": '@user.get.getMiniUser.fullName'
122+
});
123+
}
111124

112125
// Throttle submissions to one every 3 seconds
113126
disableSubmit(clickedBtn);

0 commit comments

Comments
 (0)