mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #6393 from overleaf/ta-backfill-user-properties
Backfill User Properties GitOrigin-RevId: ea2ffeaaf71d5ce41c76a1bfb2f0014006ef0d08
This commit is contained in:
parent
b2e18c92a6
commit
cfaec97a3d
5 changed files with 85 additions and 17 deletions
|
@ -1,4 +1,5 @@
|
|||
const _ = require('lodash')
|
||||
const Settings = require('@overleaf/settings')
|
||||
|
||||
/**
|
||||
* Merge feature sets coming from different sources
|
||||
|
@ -96,4 +97,18 @@ function compareFeatures(currentFeatures, expectedFeatures) {
|
|||
return mismatchReasons
|
||||
}
|
||||
|
||||
module.exports = { mergeFeatures, isFeatureSetBetter, compareFeatures }
|
||||
function getMatchedFeatureSet(features) {
|
||||
for (const [name, featureSet] of Object.entries(Settings.features)) {
|
||||
if (_.isEqual(features, featureSet)) {
|
||||
return name
|
||||
}
|
||||
}
|
||||
return 'mixed'
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
mergeFeatures,
|
||||
isFeatureSetBetter,
|
||||
compareFeatures,
|
||||
getMatchedFeatureSet,
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ async function refreshFeatures(userId, reason) {
|
|||
const features = await computeFeatures(userId)
|
||||
logger.log({ userId, features }, 'updating user features')
|
||||
|
||||
const matchedFeatureSet = _getMatchedFeatureSet(features)
|
||||
const matchedFeatureSet = FeaturesHelper.getMatchedFeatureSet(features)
|
||||
AnalyticsManager.setUserPropertyForUser(
|
||||
userId,
|
||||
'feature-set',
|
||||
|
@ -189,15 +189,6 @@ async function doSyncFromV1(v1UserId) {
|
|||
return refreshFeatures(user._id, 'sync-v1')
|
||||
}
|
||||
|
||||
function _getMatchedFeatureSet(features) {
|
||||
for (const [name, featureSet] of Object.entries(Settings.features)) {
|
||||
if (_.isEqual(features, featureSet)) {
|
||||
return name
|
||||
}
|
||||
}
|
||||
return 'mixed'
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
featuresEpochIsCurrent,
|
||||
computeFeatures: callbackify(computeFeatures),
|
||||
|
|
60
services/web/scripts/backfill_user_properties.js
Normal file
60
services/web/scripts/backfill_user_properties.js
Normal file
|
@ -0,0 +1,60 @@
|
|||
const WRITE_CONCURRENCY = parseInt(process.env.WRITE_CONCURRENCY, 10) || 10
|
||||
|
||||
const { batchedUpdateWithResultHandling } = require('./helpers/batchedUpdate')
|
||||
const { promiseMapWithLimit } = require('../app/src/util/promises')
|
||||
const SubscriptionLocator = require('../app/src/features/Subscription/SubscriptionLocator')
|
||||
const PlansLocator = require('../app/src/features/Subscription/PlansLocator')
|
||||
const FeaturesHelper = require('../app/src/features/Subscription/FeaturesHelper')
|
||||
const AnalyticsManager = require('../app/src/features/Analytics/AnalyticsManager')
|
||||
|
||||
async function getGroupSubscriptionPlanCode(userId) {
|
||||
const subscriptions =
|
||||
await SubscriptionLocator.promises.getMemberSubscriptions(userId)
|
||||
let bestPlanCode = null
|
||||
let bestFeatures = {}
|
||||
for (const subscription of subscriptions) {
|
||||
const plan = PlansLocator.findLocalPlanInSettings(subscription.planCode)
|
||||
if (
|
||||
plan &&
|
||||
FeaturesHelper.isFeatureSetBetter(plan.features, bestFeatures)
|
||||
) {
|
||||
bestPlanCode = plan.planCode
|
||||
bestFeatures = plan.features
|
||||
}
|
||||
}
|
||||
return bestPlanCode
|
||||
}
|
||||
|
||||
async function processUser(user) {
|
||||
const analyticsId = user.analyticsId || user._id
|
||||
|
||||
const groupSubscriptionPlanCode = await getGroupSubscriptionPlanCode(user._id)
|
||||
if (groupSubscriptionPlanCode) {
|
||||
await AnalyticsManager.setUserPropertyForAnalyticsId(
|
||||
analyticsId,
|
||||
'group-subscription-plan-code',
|
||||
groupSubscriptionPlanCode
|
||||
)
|
||||
}
|
||||
|
||||
const matchedFeatureSet = FeaturesHelper.getMatchedFeatureSet(user.features)
|
||||
if (matchedFeatureSet !== 'personal') {
|
||||
await AnalyticsManager.setUserPropertyForAnalyticsId(
|
||||
analyticsId,
|
||||
'feature-set',
|
||||
matchedFeatureSet
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function processBatch(_, users) {
|
||||
await promiseMapWithLimit(WRITE_CONCURRENCY, users, async user => {
|
||||
await processUser(user)
|
||||
})
|
||||
}
|
||||
|
||||
batchedUpdateWithResultHandling('users', {}, processBatch, {
|
||||
_id: true,
|
||||
analyticsId: true,
|
||||
features: true,
|
||||
})
|
|
@ -95,8 +95,14 @@ async function batchedUpdate(
|
|||
return updated
|
||||
}
|
||||
|
||||
function batchedUpdateWithResultHandling(collection, query, update) {
|
||||
batchedUpdate(collection, query, update)
|
||||
function batchedUpdateWithResultHandling(
|
||||
collection,
|
||||
query,
|
||||
update,
|
||||
projection,
|
||||
options
|
||||
) {
|
||||
batchedUpdate(collection, query, update, projection, options)
|
||||
.then(updated => {
|
||||
console.error({ updated })
|
||||
process.exit(0)
|
||||
|
|
|
@ -105,15 +105,11 @@ describe('FeaturesUpdater', function () {
|
|||
this.Modules = {
|
||||
promises: { hooks: { fire: sinon.stub().resolves() } },
|
||||
}
|
||||
this.FeaturesHelper = {
|
||||
mergeFeatures: sinon.stub().callsFake((a, b) => ({ ...a, ...b })),
|
||||
}
|
||||
|
||||
this.FeaturesUpdater = SandboxedModule.require(MODULE_PATH, {
|
||||
requires: {
|
||||
'./UserFeaturesUpdater': this.UserFeaturesUpdater,
|
||||
'./SubscriptionLocator': this.SubscriptionLocator,
|
||||
'./FeaturesHelper': this.FeaturesHelper,
|
||||
'@overleaf/settings': this.Settings,
|
||||
'../Referal/ReferalFeatures': this.ReferalFeatures,
|
||||
'./V1SubscriptionManager': this.V1SubscriptionManager,
|
||||
|
|
Loading…
Reference in a new issue