Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
coopwire-hypothesis
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
孙灵跃 Leon Sun
coopwire-hypothesis
Commits
77ea41de
Commit
77ea41de
authored
Feb 01, 2017
by
Sean Roberts
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Adding client metric tracking for interactions with annotations
parent
3b5ba2df
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
108 additions
and
16 deletions
+108
-16
analytics.js
src/sidebar/analytics.js
+21
-2
annotation.js
src/sidebar/directive/annotation.js
+40
-6
annotation-test.js
src/sidebar/directive/test/annotation-test.js
+7
-0
analytics-test.js
src/sidebar/test/analytics-test.js
+29
-5
annotation-fixtures.js
src/sidebar/test/annotation-fixtures.js
+1
-1
app-controller-test.js
src/sidebar/test/app-controller-test.js
+7
-0
widget-controller-test.js
src/sidebar/test/widget-controller-test.js
+2
-1
widget-controller.js
src/sidebar/widget-controller.js
+1
-1
No files found.
src/sidebar/analytics.js
View file @
77ea41de
...
@@ -50,8 +50,27 @@ function analytics($analytics, $window, settings) {
...
@@ -50,8 +50,27 @@ function analytics($analytics, $window, settings) {
* in our analytics. Example: 'sidebarOpened'. Use camelCase to track multiple
* in our analytics. Example: 'sidebarOpened'. Use camelCase to track multiple
* words.
* words.
*/
*/
track
:
function
(
event
){
track
:
function
(
event
,
label
,
metricValue
){
$analytics
.
eventTrack
(
event
,
options
);
$analytics
.
eventTrack
(
event
,
Object
.
assign
({},
{
label
:
label
||
undefined
,
metricValue
:
isNaN
(
metricValue
)
?
undefined
:
metricValue
,
},
options
));
},
events
:
{
ANNOTATION_CREATED
:
'annotationCreated'
,
ANNOTATION_DELETED
:
'annotationDeleted'
,
ANNOTATION_UPDATED
:
'annotationUpdated'
,
HIGHLIGHT_CREATED
:
'highlightCreated'
,
HIGHLIGHT_UPDATED
:
'highlightUpdated'
,
HIGHLIGHT_DELETED
:
'highlightDeleted'
,
PAGE_NOTE_CREATED
:
'pageNoteCreated'
,
PAGE_NOTE_UPDATED
:
'pageNoteUpdated'
,
PAGE_NOTE_DELETED
:
'pageNoteDeleted'
,
REPLY_CREATED
:
'replyCreated'
,
REPLY_UPDATED
:
'replyUpdated'
,
REPLY_DELETED
:
'replyDeleted'
,
SIDEBAR_OPENED
:
'sidebarOpened'
,
},
},
};
};
}
}
...
...
src/sidebar/directive/annotation.js
View file @
77ea41de
...
@@ -9,6 +9,7 @@ var persona = require('../filter/persona');
...
@@ -9,6 +9,7 @@ var persona = require('../filter/persona');
var
isNew
=
annotationMetadata
.
isNew
;
var
isNew
=
annotationMetadata
.
isNew
;
var
isReply
=
annotationMetadata
.
isReply
;
var
isReply
=
annotationMetadata
.
isReply
;
var
isPageNote
=
annotationMetadata
.
isPageNote
;
/** Return a human-readable error message for the given server error.
/** Return a human-readable error message for the given server error.
*
*
...
@@ -46,7 +47,7 @@ function updateModel(annotation, changes, permissions) {
...
@@ -46,7 +47,7 @@ function updateModel(annotation, changes, permissions) {
// @ngInject
// @ngInject
function
AnnotationController
(
function
AnnotationController
(
$document
,
$q
,
$rootScope
,
$scope
,
$timeout
,
$window
,
annotationUI
,
$document
,
$q
,
$rootScope
,
$scope
,
$timeout
,
$window
,
an
alytics
,
an
notationUI
,
annotationMapper
,
drafts
,
flash
,
features
,
groups
,
permissions
,
serviceUrl
,
annotationMapper
,
drafts
,
flash
,
features
,
groups
,
permissions
,
serviceUrl
,
session
,
store
,
streamer
)
{
session
,
store
,
streamer
)
{
...
@@ -56,12 +57,18 @@ function AnnotationController(
...
@@ -56,12 +57,18 @@ function AnnotationController(
/** Save an annotation to the server. */
/** Save an annotation to the server. */
function
save
(
annot
)
{
function
save
(
annot
)
{
var
saved
;
var
saved
;
if
(
annot
.
id
)
{
var
updating
=
!!
annot
.
id
;
if
(
updating
)
{
saved
=
store
.
annotation
.
update
({
id
:
annot
.
id
},
annot
);
saved
=
store
.
annotation
.
update
({
id
:
annot
.
id
},
annot
);
}
else
{
}
else
{
saved
=
store
.
annotation
.
create
({},
annot
);
saved
=
store
.
annotation
.
create
({},
annot
);
}
}
return
saved
.
then
(
function
(
savedAnnot
)
{
return
saved
.
then
(
function
(
savedAnnot
)
{
var
event
;
// Copy across internal properties which are not part of the annotation
// Copy across internal properties which are not part of the annotation
// model saved on the server
// model saved on the server
savedAnnot
.
$tag
=
annot
.
$tag
;
savedAnnot
.
$tag
=
annot
.
$tag
;
...
@@ -70,6 +77,20 @@ function AnnotationController(
...
@@ -70,6 +77,20 @@ function AnnotationController(
savedAnnot
[
k
]
=
annot
[
k
];
savedAnnot
[
k
]
=
annot
[
k
];
}
}
});
});
if
(
vm
.
isReply
()){
event
=
updating
?
analytics
.
events
.
REPLY_UPDATED
:
analytics
.
events
.
REPLY_CREATED
;
}
else
if
(
vm
.
isHighlight
()){
event
=
updating
?
analytics
.
events
.
HIGHLIGHT_UPDATED
:
analytics
.
events
.
HIGHLIGHT_CREATED
;
}
else
if
(
isPageNote
(
vm
.
annotation
))
{
event
=
updating
?
analytics
.
events
.
PAGE_NOTE_UPDATED
:
analytics
.
events
.
PAGE_NOTE_CREATED
;
}
else
{
event
=
updating
?
analytics
.
events
.
ANNOTATION_UPDATED
:
analytics
.
events
.
ANNOTATION_CREATED
;
}
analytics
.
track
(
event
);
return
savedAnnot
;
return
savedAnnot
;
});
});
}
}
...
@@ -213,8 +234,22 @@ function AnnotationController(
...
@@ -213,8 +234,22 @@ function AnnotationController(
errorMessage
(
reason
),
'Deleting annotation failed'
);
errorMessage
(
reason
),
'Deleting annotation failed'
);
};
};
$scope
.
$apply
(
function
()
{
$scope
.
$apply
(
function
()
{
annotationMapper
.
deleteAnnotation
(
vm
.
annotation
).
then
(
annotationMapper
.
deleteAnnotation
(
vm
.
annotation
).
then
(
function
(){
null
,
onRejected
);
var
event
;
if
(
vm
.
isReply
()){
event
=
analytics
.
events
.
REPLY_DELETED
;
}
else
if
(
vm
.
isHighlight
()){
event
=
analytics
.
events
.
HIGHLIGHT_DELETED
;
}
else
if
(
isPageNote
(
vm
.
annotation
)){
event
=
analytics
.
events
.
PAGE_NOTE_DELETED
;
}
else
{
event
=
analytics
.
events
.
ANNOTATION_DELETED
;
}
analytics
.
track
(
event
);
},
onRejected
);
});
});
}
}
},
true
);
},
true
);
...
@@ -291,8 +326,7 @@ function AnnotationController(
...
@@ -291,8 +326,7 @@ function AnnotationController(
// example there's no vm.annotation.highlight: true. Instead a highlight is
// example there's no vm.annotation.highlight: true. Instead a highlight is
// defined as an annotation that isn't a page note or a reply and that
// defined as an annotation that isn't a page note or a reply and that
// has no text or tags.
// has no text or tags.
var
isPageNote
=
(
vm
.
annotation
.
target
||
[]).
length
===
0
;
return
(
!
isPageNote
(
vm
.
annotation
)
&&
!
isReply
(
vm
.
annotation
)
&&
!
vm
.
hasContent
());
return
(
!
isPageNote
&&
!
isReply
(
vm
.
annotation
)
&&
!
vm
.
hasContent
());
}
}
};
};
...
...
src/sidebar/directive/test/annotation-test.js
View file @
77ea41de
...
@@ -82,6 +82,7 @@ describe('annotation', function() {
...
@@ -82,6 +82,7 @@ describe('annotation', function() {
var
$scope
;
var
$scope
;
var
$timeout
;
var
$timeout
;
var
$window
;
var
$window
;
var
fakeAnalytics
;
var
fakeAnnotationMapper
;
var
fakeAnnotationMapper
;
var
fakeDrafts
;
var
fakeDrafts
;
var
fakeFlash
;
var
fakeFlash
;
...
@@ -121,6 +122,11 @@ describe('annotation', function() {
...
@@ -121,6 +122,11 @@ describe('annotation', function() {
beforeEach
(
angular
.
mock
.
module
(
function
(
$provide
)
{
beforeEach
(
angular
.
mock
.
module
(
function
(
$provide
)
{
sandbox
=
sinon
.
sandbox
.
create
();
sandbox
=
sinon
.
sandbox
.
create
();
fakeAnalytics
=
{
track
:
sandbox
.
stub
(),
events
:
{},
};
fakeAnnotationMapper
=
{
fakeAnnotationMapper
=
{
createAnnotation
:
sandbox
.
stub
().
returns
({
createAnnotation
:
sandbox
.
stub
().
returns
({
permissions
:
{
permissions
:
{
...
@@ -195,6 +201,7 @@ describe('annotation', function() {
...
@@ -195,6 +201,7 @@ describe('annotation', function() {
hasPendingDeletion
:
sinon
.
stub
(),
hasPendingDeletion
:
sinon
.
stub
(),
};
};
$provide
.
value
(
'analytics'
,
fakeAnalytics
);
$provide
.
value
(
'annotationMapper'
,
fakeAnnotationMapper
);
$provide
.
value
(
'annotationMapper'
,
fakeAnnotationMapper
);
$provide
.
value
(
'annotationUI'
,
fakeAnnotationUI
);
$provide
.
value
(
'annotationUI'
,
fakeAnnotationUI
);
$provide
.
value
(
'drafts'
,
fakeDrafts
);
$provide
.
value
(
'drafts'
,
fakeDrafts
);
...
...
src/sidebar/test/analytics-test.js
View file @
77ea41de
...
@@ -2,6 +2,14 @@
...
@@ -2,6 +2,14 @@
var
analyticsService
=
require
(
'../analytics'
);
var
analyticsService
=
require
(
'../analytics'
);
var
createEventObj
=
function
(
override
){
return
{
category
:
override
.
category
,
label
:
override
.
label
,
metricValue
:
override
.
metricValue
,
};
};
describe
(
'analytics'
,
function
()
{
describe
(
'analytics'
,
function
()
{
var
$analyticsStub
;
var
$analyticsStub
;
...
@@ -32,31 +40,47 @@ describe('analytics', function () {
...
@@ -32,31 +40,47 @@ describe('analytics', function () {
var
validTypes
=
[
'chrome-extension'
,
'embed'
,
'bookmarklet'
,
'via'
];
var
validTypes
=
[
'chrome-extension'
,
'embed'
,
'bookmarklet'
,
'via'
];
validTypes
.
forEach
(
function
(
appType
,
index
){
validTypes
.
forEach
(
function
(
appType
,
index
){
analyticsService
(
$analyticsStub
,
$windowStub
,
{
appType
:
appType
}).
track
(
'event'
+
index
);
analyticsService
(
$analyticsStub
,
$windowStub
,
{
appType
:
appType
}).
track
(
'event'
+
index
);
assert
.
deepEqual
(
eventTrackStub
.
args
[
index
],
[
'event'
+
index
,
{
category
:
appType
}
]);
assert
.
deepEqual
(
eventTrackStub
.
args
[
index
],
[
'event'
+
index
,
createEventObj
({
category
:
appType
})
]);
});
});
});
});
it
(
'sets category as embed if no other matches can be made'
,
function
()
{
it
(
'sets category as embed if no other matches can be made'
,
function
()
{
analyticsService
(
$analyticsStub
,
$windowStub
).
track
(
'eventA'
);
analyticsService
(
$analyticsStub
,
$windowStub
).
track
(
'eventA'
);
assert
.
deepEqual
(
eventTrackStub
.
args
[
0
],
[
'eventA'
,
{
category
:
'embed'
}
]);
assert
.
deepEqual
(
eventTrackStub
.
args
[
0
],
[
'eventA'
,
createEventObj
({
category
:
'embed'
})
]);
});
});
it
(
'sets category as via if url matches the via uri pattern'
,
function
()
{
it
(
'sets category as via if url matches the via uri pattern'
,
function
()
{
$windowStub
.
document
.
referrer
=
'https://via.hypothes.is/'
;
$windowStub
.
document
.
referrer
=
'https://via.hypothes.is/'
;
analyticsService
(
$analyticsStub
,
$windowStub
).
track
(
'eventA'
);
analyticsService
(
$analyticsStub
,
$windowStub
).
track
(
'eventA'
);
assert
.
deepEqual
(
eventTrackStub
.
args
[
0
],
[
'eventA'
,
{
category
:
'via'
}
]);
assert
.
deepEqual
(
eventTrackStub
.
args
[
0
],
[
'eventA'
,
createEventObj
({
category
:
'via'
})
]);
// match staging as well
// match staging as well
$windowStub
.
document
.
referrer
=
'https://qa-via.hypothes.is/'
;
$windowStub
.
document
.
referrer
=
'https://qa-via.hypothes.is/'
;
analyticsService
(
$analyticsStub
,
$windowStub
).
track
(
'eventB'
);
analyticsService
(
$analyticsStub
,
$windowStub
).
track
(
'eventB'
);
assert
.
deepEqual
(
eventTrackStub
.
args
[
1
],
[
'eventB'
,
{
category
:
'via'
}
]);
assert
.
deepEqual
(
eventTrackStub
.
args
[
1
],
[
'eventB'
,
createEventObj
({
category
:
'via'
})
]);
});
});
it
(
'sets category as chrome-extension if protocol matches chrome-extension:'
,
function
()
{
it
(
'sets category as chrome-extension if protocol matches chrome-extension:'
,
function
()
{
$windowStub
.
location
.
protocol
=
'chrome-extension:'
;
$windowStub
.
location
.
protocol
=
'chrome-extension:'
;
analyticsService
(
$analyticsStub
,
$windowStub
).
track
(
'eventA'
);
analyticsService
(
$analyticsStub
,
$windowStub
).
track
(
'eventA'
);
assert
.
deepEqual
(
eventTrackStub
.
args
[
0
],
[
'eventA'
,
{
category
:
'chrome-extension'
}
]);
assert
.
deepEqual
(
eventTrackStub
.
args
[
0
],
[
'eventA'
,
createEventObj
({
category
:
'chrome-extension'
})
]);
});
});
});
});
it
(
'allows custom labels to be sent for an event'
,
function
()
{
analyticsService
(
$analyticsStub
,
$windowStub
,
{
appType
:
'embed'
}).
track
(
'eventA'
,
'labelA'
);
assert
.
deepEqual
(
eventTrackStub
.
args
[
0
],
[
'eventA'
,
createEventObj
({
category
:
'embed'
,
label
:
'labelA'
})]);
});
it
(
'allows custom metricValues to be sent for an event'
,
function
()
{
analyticsService
(
$analyticsStub
,
$windowStub
,
{
appType
:
'embed'
}).
track
(
'eventA'
,
null
,
242.2
);
assert
.
deepEqual
(
eventTrackStub
.
args
[
0
],
[
'eventA'
,
createEventObj
({
category
:
'embed'
,
metricValue
:
242.2
})]);
});
it
(
'allows custom metricValues and labels to be sent for an event'
,
function
()
{
analyticsService
(
$analyticsStub
,
$windowStub
,
{
appType
:
'embed'
}).
track
(
'eventA'
,
'labelabc'
,
242.2
);
assert
.
deepEqual
(
eventTrackStub
.
args
[
0
],
[
'eventA'
,
createEventObj
({
category
:
'embed'
,
label
:
'labelabc'
,
metricValue
:
242.2
})]);
});
});
});
src/sidebar/test/annotation-fixtures.js
View file @
77ea41de
...
@@ -71,7 +71,7 @@ function oldAnnotation() {
...
@@ -71,7 +71,7 @@ function oldAnnotation() {
return
{
return
{
id
:
'annotation_id'
,
id
:
'annotation_id'
,
$highlight
:
undefined
,
$highlight
:
undefined
,
target
:
[
'foo'
,
'bar'
],
target
:
[
{
source
:
'source'
,
'selector'
:
[]
}
],
references
:
[],
references
:
[],
text
:
'This is my annotation'
,
text
:
'This is my annotation'
,
tags
:
[
'tag_1'
,
'tag_2'
],
tags
:
[
'tag_1'
,
'tag_2'
],
...
...
src/sidebar/test/app-controller-test.js
View file @
77ea41de
...
@@ -12,6 +12,7 @@ describe('AppController', function () {
...
@@ -12,6 +12,7 @@ describe('AppController', function () {
var
$rootScope
=
null
;
var
$rootScope
=
null
;
var
fakeAnnotationMetadata
=
null
;
var
fakeAnnotationMetadata
=
null
;
var
fakeAnnotationUI
=
null
;
var
fakeAnnotationUI
=
null
;
var
fakeAnalytics
=
null
;
var
fakeAuth
=
null
;
var
fakeAuth
=
null
;
var
fakeDrafts
=
null
;
var
fakeDrafts
=
null
;
var
fakeFeatures
=
null
;
var
fakeFeatures
=
null
;
...
@@ -58,6 +59,11 @@ describe('AppController', function () {
...
@@ -58,6 +59,11 @@ describe('AppController', function () {
clearSelectedAnnotations
:
sandbox
.
spy
(),
clearSelectedAnnotations
:
sandbox
.
spy
(),
};
};
fakeAnalytics
=
{
track
:
sandbox
.
stub
(),
events
:
{},
};
fakeAuth
=
{};
fakeAuth
=
{};
fakeDrafts
=
{
fakeDrafts
=
{
...
@@ -107,6 +113,7 @@ describe('AppController', function () {
...
@@ -107,6 +113,7 @@ describe('AppController', function () {
$provide
.
value
(
'annotationUI'
,
fakeAnnotationUI
);
$provide
.
value
(
'annotationUI'
,
fakeAnnotationUI
);
$provide
.
value
(
'auth'
,
fakeAuth
);
$provide
.
value
(
'auth'
,
fakeAuth
);
$provide
.
value
(
'analytics'
,
fakeAnalytics
);
$provide
.
value
(
'drafts'
,
fakeDrafts
);
$provide
.
value
(
'drafts'
,
fakeDrafts
);
$provide
.
value
(
'features'
,
fakeFeatures
);
$provide
.
value
(
'features'
,
fakeFeatures
);
$provide
.
value
(
'frameSync'
,
fakeFrameSync
);
$provide
.
value
(
'frameSync'
,
fakeFrameSync
);
...
...
src/sidebar/test/widget-controller-test.js
View file @
77ea41de
...
@@ -71,7 +71,8 @@ describe('WidgetController', function () {
...
@@ -71,7 +71,8 @@ describe('WidgetController', function () {
sandbox
=
sinon
.
sandbox
.
create
();
sandbox
=
sinon
.
sandbox
.
create
();
fakeAnalytics
=
{
fakeAnalytics
=
{
track
:
sandbox
.
spy
(),
track
:
sandbox
.
stub
(),
events
:
{},
};
};
fakeAnnotationMapper
=
{
fakeAnnotationMapper
=
{
...
...
src/sidebar/widget-controller.js
View file @
77ea41de
...
@@ -202,7 +202,7 @@ module.exports = function WidgetController(
...
@@ -202,7 +202,7 @@ module.exports = function WidgetController(
$scope
.
$on
(
'sidebarOpened'
,
function
()
{
$scope
.
$on
(
'sidebarOpened'
,
function
()
{
analytics
.
track
(
'sidebarOpened'
);
analytics
.
track
(
analytics
.
events
.
SIDEBAR_OPENED
);
streamer
.
connect
();
streamer
.
connect
();
});
});
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment