mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
use anlaytis api
- don’t talk to postgres - show recent blog post announcments - proxy all events to analytics api
This commit is contained in:
parent
8763e444e9
commit
5a13ee1077
13 changed files with 222 additions and 53 deletions
|
@ -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
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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
|
24
services/web/app/coffee/Features/Blog/BlogHandler.coffee
Normal file
24
services/web/app/coffee/Features/Blog/BlogHandler.coffee
Normal 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)
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
20
services/web/public/coffee/main/announcements.coffee
Normal file
20
services/web/public/coffee/main/announcements.coffee
Normal 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
|
|
@ -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)->
|
||||
|
|
|
@ -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()
|
Loading…
Reference in a new issue