use anlaytis api

- don’t talk to postgres
- show recent blog post announcments
- proxy all events to analytics api
This commit is contained in:
Henry Oswald 2016-11-11 17:03:01 +00:00
parent 8763e444e9
commit 5a13ee1077
13 changed files with 222 additions and 53 deletions

View file

@ -1,43 +1,36 @@
Settings = require "settings-sharelatex"
settings = require "settings-sharelatex"
logger = require "logger-sharelatex"
_ = require "underscore"
request = require "request"
if !Settings.analytics?.postgres?
module.exports =
recordEvent: (user_id, event, segmentation, callback = () ->) ->
logger.log {user_id, event, segmentation}, "no event tracking configured, logging event"
callback()
else
Sequelize = require "sequelize"
options = _.extend {logging:false}, Settings.analytics.postgres
sequelize = new Sequelize(
Settings.analytics.postgres.database,
Settings.analytics.postgres.username,
Settings.analytics.postgres.password,
options
)
Event = sequelize.define("Event", {
user_id: Sequelize.STRING,
event: Sequelize.STRING,
segmentation: Sequelize.JSONB
})
module.exports =
recordEvent: (user_id, event, segmentation = {}, callback = (error) ->) ->
if user_id? and typeof(user_id) != "string"
user_id = user_id.toString()
if user_id == Settings.smokeTest?.userId
# Don't record smoke tests analytics
return callback()
Event
.create({ user_id, event, segmentation })
.then(
(result) -> callback(),
(error) ->
logger.err {err: error, user_id, event, segmentation}, "error recording analytics event"
callback(error)
)
sync: () -> sequelize.sync()
module.exports =
recordEvent: (user_id, event, segmentation = {}, callback = (error) ->) ->
opts =
body:
event:event
segmentation:segmentation
json:true
method:"POST"
timeout:1000
url: "#{settings.apis.analytics.url}/user/#{user_id}/event"
request opts, callback
getLastOccurance: (user_id, event, callback = (error) ->) ->
opts =
body:
event:event
json:true
method:"POST"
timeout:1000
url: "#{settings.apis.analytics.url}/user/#{user_id}/event/last_occurnace"
request opts, (err, response, body)->
if err?
console.log response, opts
logger.err {user_id, err}, "error getting last occurance of event"
return callback err
else
return callback null, body

View file

@ -0,0 +1,21 @@
AnnouncementsHandler = require("./AnnouncementsHandler")
AuthenticationController = require("../Authentication/AuthenticationController")
logger = require("logger-sharelatex")
module.exports =
getUndreadAnnouncements: (req, res, next)->
user_id = AuthenticationController.getLoggedInUserId(req)
logger.log {user_id}, "getting unread announcements"
AnnouncementsHandler.getUnreadAnnouncements user_id, (err, announcements)->
if err?
logger.err {err, user_id}, "unable to get unread announcements"
next(err)
else
res.json announcements

View file

@ -0,0 +1,36 @@
AnalyticsManager = require("../Analytics/AnalyticsManager")
BlogHandler = require("../Blog/BlogHandler")
async = require("async")
_ = require("lodash")
logger = require("logger-sharelatex")
settings = require("settings-sharelatex")
module.exports =
getUnreadAnnouncements : (user_id, callback = (err, announcements)->)->
if !settings?.apis?.analytics?.url? or !settings.apis.blog.url?
return callback null, []
async.parallel {
lastEvent: (cb)->
AnalyticsManager.getLastOccurance user_id, "announcement-alert-dismissed", cb
announcements: (cb)->
BlogHandler.getLatestAnnouncements cb
}, (err, results)->
if err?
logger.err err:err, user_id:user_id, "error getting unread announcements"
return callback(err)
announcements = _.sortBy(results.announcements, "date").reverse()
lastSeenBlogId = results?.lastEvent?.segmentation?.blogPostId
announcementIndex = _.findIndex announcements, (announcement)->
announcement.id == lastSeenBlogId
if announcementIndex != -1
announcements = announcements.slice(0, announcementIndex)
logger.log announcementsLength:announcements?.length, user_id:user_id, "returning announcements"
callback null, announcements

View file

@ -0,0 +1,24 @@
request = require "request"
settings = require "settings-sharelatex"
_ = require("underscore")
logger = require "logger-sharelatex"
module.exports = BlogHandler =
getLatestAnnouncements: (callback)->
blogUrl = "#{settings.apis.blog.url}/blog/latestannouncements.json"
opts =
url:blogUrl
json:true
timeout:500
request.get opts, (err, res, announcements)->
if err?
return callback err
if res.statusCode != 200
return callback("blog announcement returned non 200")
logger.log announcementsLength: announcements?.length, "announcements returned"
announcements = _.map announcements, (announcement)->
announcement.url = "/blog#{announcement.url}"
announcement.date = new Date(announcement.date)
return announcement
callback(err, announcements)

View file

@ -20,6 +20,8 @@ ProjectGetter = require("./ProjectGetter")
PrivilegeLevels = require("../Authorization/PrivilegeLevels")
AuthenticationController = require("../Authentication/AuthenticationController")
PackageVersions = require("../../infrastructure/PackageVersions")
BlogHandler = require("../Blog/BlogHandler")
module.exports = ProjectController =
@ -153,6 +155,8 @@ module.exports = ProjectController =
return next(err)
logger.log results:results, user_id:user_id, "rendering project list"
tags = results.tags[0]
notifications = require("underscore").map results.notifications, (notification)->
notification.html = req.i18n.translate(notification.templateKey, notification.messageOpts)
return notification

View file

@ -39,6 +39,7 @@ ReferencesController = require('./Features/References/ReferencesController')
AuthorizationMiddlewear = require('./Features/Authorization/AuthorizationMiddlewear')
BetaProgramController = require('./Features/BetaProgram/BetaProgramController')
AnalyticsRouter = require('./Features/Analytics/AnalyticsRouter')
AnnouncementsController = require("./Features/Announcements/AnnouncementsController")
logger = require("logger-sharelatex")
_ = require("underscore")
@ -187,6 +188,9 @@ module.exports = class Router
webRouter.get '/notifications', AuthenticationController.requireLogin(), NotificationsController.getAllUnreadNotifications
webRouter.delete '/notifications/:notification_id', AuthenticationController.requireLogin(), NotificationsController.markNotificationAsRead
webRouter.get '/announcements', AuthenticationController.requireLogin(), AnnouncementsController.getUndreadAnnouncements
# Deprecated in favour of /internal/project/:project_id but still used by versioning
apiRouter.get '/project/:project_id/details', AuthenticationController.httpAuth, ProjectApiController.getProjectDetails

View file

@ -10,7 +10,7 @@ block content
window.data = {
projects: !{JSON.stringify(projects).replace(/\//g, '\\/')},
tags: !{JSON.stringify(tags).replace(/\//g, '\\/')},
notifications: !{JSON.stringify(notifications).replace(/\//g, '\\/')}
notifications: !{JSON.stringify(notifications).replace(/\//g, '\\/')},
};
window.algolia = {
institutions: {
@ -21,7 +21,11 @@ block content
.content.content-alt(ng-controller="ProjectPageController")
.container
div(ng-controller="AnnouncementsController", ng-cloak)
.alert.alert-success(ng-show="dataRecived")
a(href, ng-click="openLink()") {{title}} and {{totalAnnouncements}} others
.row(ng-cloak)
span(ng-if="projects.length > 0")
aside.col-md-2.col-xs-3

View file

@ -108,7 +108,8 @@ module.exports = settings =
# url: "http://localhost:3040"
notifications:
url: "http://localhost:3042"
templates:
user_id: process.env.TEMPLATES_USER_ID or "5395eb7aad1f29a88756c7f2"
showSocialButtons: false

View file

@ -25,6 +25,7 @@
"dateformat": "1.0.4-1.2.3",
"express": "4.13.0",
"express-session": "1.11.3",
"feedparser": "^1.1.5",
"grunt": "^0.4.5",
"heapdump": "^0.3.7",
"http-proxy": "^1.8.1",
@ -49,8 +50,6 @@
"passport": "^0.3.2",
"passport-ldapauth": "^0.6.0",
"passport-local": "^1.0.0",
"pg": "^6.0.3",
"pg-hstore": "^2.3.2",
"redback": "0.4.0",
"redis": "0.10.1",
"redis-sharelatex": "0.0.9",

View file

@ -14,6 +14,7 @@ define [
"main/subscription-dashboard"
"main/new-subscription"
"main/annual-upgrade"
"main/announcements"
"main/register-users"
"main/subscription/group-subscription-invite-controller"
"main/contact-us"

View file

@ -0,0 +1,20 @@
define [
"base"
], (App) ->
App.controller "AnnouncementsController", ($scope, $http, event_tracking, $window) ->
$scope.dataRecived = false
announcement = null
$http.get("/announcements").success (announcements) ->
if announcements?[0]?
announcement = announcements[0]
$scope.title = announcement.title
$scope.totalAnnouncements = announcements.length
$scope.dataRecived = true
dismissannouncement = ->
event_tracking.sendMB "announcement-alert-dismissed", {blogPostId:announcement.id}
$scope.openLink = ->
dismissannouncement()
$window.location.href = announcement.url

View file

@ -53,16 +53,6 @@ define [
@sendMB key, segmentation
}
# App.directive "countlyTrack", () ->
# return {
# restrict: "A"
# scope: false,
# link: (scope, el, attrs) ->
# eventKey = attrs.countlyTrack
# if (eventKey?)
# el.on "click", () ->
# console.log eventKey
# }
#header
$('.navbar a').on "click", (e)->

View file

@ -0,0 +1,72 @@
should = require('chai').should()
SandboxedModule = require('sandboxed-module')
assert = require('assert')
path = require('path')
modulePath = path.join __dirname, '../../../../app/js/Features/Announcements/AnnouncementsHandler'
sinon = require("sinon")
expect = require("chai").expect
describe 'AnnouncementsHandler', ->
beforeEach ->
@user_id = "some_id"
@AnalyticsManager =
getLastOccurance: sinon.stub()
@BlogHandler =
getLatestAnnouncements:sinon.stub()
@handler = SandboxedModule.require modulePath, requires:
"../Analytics/AnalyticsManager":@AnalyticsManager
"../Blog/BlogHandler":@BlogHandler
"logger-sharelatex":
log:->
describe "getUnreadAnnouncements", ->
beforeEach ->
@stubbedAnnouncements = [
{
date: new Date(1478836800000),
id: '/2016/11/01/introducting-latex-code-checker'
}, {
date: new Date(1308369600000),
id: '/2013/08/02/thesis-series-pt1'
}, {
date: new Date(1108369600000),
id: '/2011/08/04/somethingelse'
}, {
date: new Date(1208369600000),
id: '/2014/04/12/title-date-irrelivant'
}
]
@BlogHandler.getLatestAnnouncements.callsArgWith(0, null, @stubbedAnnouncements)
it "should return all announcements if there are no getLastOccurance", (done)->
@AnalyticsManager.getLastOccurance.callsArgWith(2, null, [])
@handler.getUnreadAnnouncements @user_id, (err, announcements)=>
announcements.length.should.equal 4
done()
it "should should be sorted again to ensure correct order", (done)->
@AnalyticsManager.getLastOccurance.callsArgWith(2, null, [])
@handler.getUnreadAnnouncements @user_id, (err, announcements)=>
announcements[3].should.equal @stubbedAnnouncements[2]
announcements[2].should.equal @stubbedAnnouncements[3]
announcements[1].should.equal @stubbedAnnouncements[1]
announcements[0].should.equal @stubbedAnnouncements[0]
done()
it "should return ones older than the last blog id", (done)->
@AnalyticsManager.getLastOccurance.callsArgWith(2, null, {segmentation:{blogPostId:"/2014/04/12/title-date-irrelivant"}})
@handler.getUnreadAnnouncements @user_id, (err, announcements)=>
announcements.length.should.equal 2
announcements[0].id.should.equal @stubbedAnnouncements[0].id
announcements[1].id.should.equal @stubbedAnnouncements[1].id
done()
it "should return none when the latest id is the first element", (done)->
@AnalyticsManager.getLastOccurance.callsArgWith(2, null, {segmentation:{blogPostId:"/2016/11/01/introducting-latex-code-checker"}})
@handler.getUnreadAnnouncements @user_id, (err, announcements)=>
announcements.length.should.equal 0
done()