Skip to content

feat: track user events in Countly #282

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Aug 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c8cdf13
add test pages
terichadbourne Aug 5, 2019
7e53725
track link clicks and scrolls
terichadbourne Aug 6, 2019
9776e9a
add hash to path
terichadbourne Aug 7, 2019
40e5920
track lesson passed for exercise and text
terichadbourne Aug 7, 2019
47359c9
Merge branch 'add-test-pages' into feat/countly-event-tracking
terichadbourne Aug 7, 2019
9b40b6d
track lesson passed for mult choice
terichadbourne Aug 7, 2019
da6d078
remove unused console logs
terichadbourne Aug 7, 2019
d3ffda0
enable testing from localhost
terichadbourne Aug 7, 2019
da2af72
revert to default event count
terichadbourne Aug 7, 2019
42bf9bc
add test pages for all lesson types
terichadbourne Aug 7, 2019
bb703e0
Merge branch 'add-test-pages' into feat/countly-event-tracking
terichadbourne Aug 7, 2019
6cc31ff
feat: add reset and submit events
fsdiogo Aug 9, 2019
b3d3f2f
feat: track wrong choices/code
fsdiogo Aug 12, 2019
03ca584
feat: tutorial passed event
fsdiogo Aug 12, 2019
d2edaec
track tutorialPassed in countly
terichadbourne Aug 12, 2019
6829ef3
chore: sync with code
fsdiogo Aug 14, 2019
cf02974
chore: override getviewurl
fsdiogo Aug 14, 2019
2428ec8
merge code w new domain into branch
terichadbourne Aug 15, 2019
c3db07b
chore: sync with master
fsdiogo Aug 19, 2019
c89eb42
fix: ignore countly when using e2e test
fsdiogo Aug 19, 2019
aab498b
fix: e2e test for tuts with 10+ lessons
fsdiogo Aug 20, 2019
ebc19c4
fix broken tutorial segment
terichadbourne Aug 20, 2019
2c9ad26
chore: remove test tutorial
terichadbourne Aug 20, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions cypress/integration/tutorials.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ function viewSolutionsAndSubmitAll ({ tutorialName, lessonCount, hasResources =
cy.get(`[href="#/${tutorialName}/01"]`).click()
})
for (let i = 1; i <= lessonCount; i++) {
it(`should view the solution and pass test ${i}`, function () {
cy.url().should('include', `#/${tutorialName}/0${i}`)
let lessonNr = i.toString().padStart(2, 0)
it(`should view the solution and pass test ${lessonNr}`, function () {
cy.url().should('include', `#/${tutorialName}/${lessonNr}`)
cy.get('[data-cy=code-editor-ready]').should('be.visible') // wait for editor to be updated
cy.get('[data-cy=view-solution]').click()
cy.get('[data-cy=solution-editor-ready]').should('be.visible') // wait for editor to be updated
Expand Down
11 changes: 9 additions & 2 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,26 @@
const COUNTLY_URL = 'https://countly.proto.school';

(function(){
if (!_dntEnabled()) {
if (!window.Cypress && !_dntEnabled()) {
// Provide countly initialization parameters
Countly.app_key = location.hostname === 'proto.school' ? COUNTLY_KEY_PROTOSCHOOL : COUNTLY_KEY_PROTOSCHOOL_TEST;
Countly.url = COUNTLY_URL;
// Choose what to track
Countly.q.push(['track_sessions']);
Countly.q.push(['track_pageview']);
Countly.q.push(['track_clicks']);
Countly.q.push(['track_scrolls']);
Countly.q.push(['track_links']);
// Load countly script asynchronously
var cly = document.createElement('script'); cly.type = 'text/javascript';
cly.async = true;
cly.src = 'https://countly.proto.school/sdk/web/countly.min.js';
cly.onload = function(){Countly.init()};
cly.onload = function(){
Countly.init()
Countly.getViewUrl = function () {
return location.pathname + location.search + location.hash;
}
};
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(cly, s);
}
})();
Expand Down
52 changes: 51 additions & 1 deletion src/components/Lesson.vue
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ import Info from './Info.vue'
import Validator from './Validator.vue'
import CID from 'cids'
import marked from 'marked'
import { EVENTS } from '../static/countly'

const hljs = require('highlight.js/lib/highlight.js')
hljs.registerLanguage('js', require('highlight.js/lib/languages/javascript'))
Expand Down Expand Up @@ -204,6 +205,7 @@ export default {
parsedConcepts: marked(self.$attrs.concepts || ''),
cacheKey: 'cached' + self.$route.path,
cachedStateMsg: '',
tutorialPath: self.$route.path.split('/')[1],
lessonKey: 'passed' + self.$route.path,
lessonPassed: !!localStorage['passed' + self.$route.path],
lessonTitle: self.$attrs.lessonTitle,
Expand Down Expand Up @@ -300,6 +302,9 @@ export default {
this.lessonPassed = !!localStorage[this.lessonKey]
this.isSubmitting = false
this.clearPassed()
if (auto !== true) {
this.trackEvent(EVENTS.CODE_SUBMIT_WRONG)
}
return
}
// Hide the solution
Expand All @@ -315,10 +320,17 @@ export default {
}
if (output.test.success) {
localStorage[this.lessonKey] = 'passed'
if (auto !== true) {
// track lesson passed if it has an exercise (incl file ones)
this.trackEvent(EVENTS.LESSON_PASSED)
this.isTutorialPassed()
}
} else {
this.clearPassed()
if (auto !== true) {
this.trackEvent(EVENTS.CODE_SUBMIT_WRONG)
}
}

this.lessonPassed = !!localStorage[this.lessonKey]
this.isSubmitting = false
},
Expand Down Expand Up @@ -350,6 +362,7 @@ export default {
this.clearPassed()
delete this.output.test
this.showUploadInfo = false
this.trackEvent(EVENTS.CODE_RESET)
},
resetFileUpload: function () {
this.uploadedFiles = false
Expand All @@ -359,11 +372,35 @@ export default {
clearPassed: function () {
delete localStorage[this.lessonKey]
this.lessonPassed = !!localStorage[this.lessonKey]
delete localStorage[`passed/${this.tutorialPath}`]
},
loadCodeFromCache: function () {
this.code = localStorage[this.cacheKey]
this.editor.setValue(this.code)
},
isTutorialPassed: function () {
for (let i = 1; i <= this.lessonsInTutorial; i++) {
let lessonNr = i.toString().padStart(2, 0)
const lsKey = `passed/${this.tutorialPath}/${lessonNr}`
if (localStorage[lsKey] !== 'passed') {
return false
}
}
localStorage[`passed/${this.tutorialPath}`] = 'passed'
this.trackEvent(EVENTS.TUTORIAL_PASSED)
return true
},
trackEvent: function (event, opts = {}) {
window.Countly.q.push(['add_event', {
'key': event,
'segmentation': {
'tutorial': this.tutorialShortname,
'lessonNumber': this.lessonNumber,
'path': this.$route.path,
...opts
}
}])
},
onMounted: function (editor) {
// runs on page load, NOT on every keystroke in editor
this.editor = editor
Expand Down Expand Up @@ -405,8 +442,16 @@ export default {
if (this.output.test.success) {
localStorage[this.lessonKey] = 'passed'
this.lessonPassed = !!localStorage[this.lessonKey]
if (result.auto !== true) {
// track multiple choice lesson passed if not on page load
this.trackEvent(EVENTS.LESSON_PASSED)
this.isTutorialPassed()
}
} else {
this.clearPassed()
if (result.auto !== true) {
this.trackEvent(EVENTS.CHOICE_SUBMIT_WRONG, { wrongChoice: result.selected })
}
}
},
next: function () {
Expand All @@ -415,6 +460,11 @@ export default {
} else {
localStorage[this.lessonKey] = 'passed'
this.lessonPassed = !!localStorage[this.lessonKey]
// track passed lesson if text only
if (!this.isMultipleChoiceLesson) {
this.trackEvent(EVENTS.LESSON_PASSED)
this.isTutorialPassed()
}
}
let current = this.lessonNumber

Expand Down
8 changes: 4 additions & 4 deletions src/components/Quiz.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default {
}
},
mounted: function () {
this.handleRadioClick()
this.handleRadioClick(true)
},
computed: {
correctChoice: function () {
Expand All @@ -36,13 +36,13 @@ export default {
}
},
methods: {
handleRadioClick () {
handleRadioClick (auto = false) {
let result = null
if (this.selected !== '') {
if (parseInt(this.selected) === this.correctChoice) {
result = { success: this.choices[this.selected].feedback, selected: this.selected }
result = { success: this.choices[this.selected].feedback, selected: this.selected, auto: auto }
} else {
result = { fail: this.choices[this.selected].feedback, selected: this.selected }
result = { fail: this.choices[this.selected].feedback, selected: this.selected, auto: auto }
}
this.$emit('handleChoice', result)
}
Expand Down
6 changes: 4 additions & 2 deletions src/components/Tutorial.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<template v-else>
{{tutorial.title}}
</template>
<span v-if="isTutorialPassed" class="ml1">🏆</span>
</h2>
<p class="f5 fw5 ma0 pt2 lh-copy charcoal-muted">{{tutorial.description}}</p>
<ul class="mv4 pa0 f5" style="list-style-type: none; background: rgba(11, 58, 82, 5%)">
Expand Down Expand Up @@ -45,10 +46,11 @@ export default {
components: {
LessonLink
},
data: () => {
data: self => {
return {
ipfsLogo: ipfsLogo,
libp2pLogo: libp2pLogo
libp2pLogo: libp2pLogo,
isTutorialPassed: !!localStorage[`passed/${self.tutorial.lessons[0].to.split('/')[1]}`]
}
},
computed: {
Expand Down
2 changes: 1 addition & 1 deletion src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ const router = new VueRouter({
// track page view via Countly when route changes
router.afterEach((to) => {
if (!window.Countly) return
window.Countly.q.push(['track_pageview', to.path])
window.Countly.q.push(['track_pageview', '/#' + to.path])
})

Vue.config.productionTip = false
Expand Down
7 changes: 7 additions & 0 deletions src/static/countly.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const EVENTS = {
CODE_RESET: 'resetCode',
CODE_SUBMIT_WRONG: 'submitWrongCode',
CHOICE_SUBMIT_WRONG: 'submitWrongChoice',
LESSON_PASSED: 'lessonPassed',
TUTORIAL_PASSED: 'tutorialPassed'
}