backfill_mixpanel_user_properties.mjs 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import '../app/src/models/User.js'
  2. import BatchedUpdateModule from './helpers/batchedUpdate.mjs'
  3. import { promiseMapWithLimit } from '@overleaf/promise-utils'
  4. import { getQueue } from '../app/src/infrastructure/Queues.js'
  5. import SubscriptionLocator from '../app/src/Features/Subscription/SubscriptionLocator.js'
  6. import PlansLocator from '../app/src/Features/Subscription/PlansLocator.js'
  7. import FeaturesHelper from '../app/src/Features/Subscription/FeaturesHelper.js'
  8. const { batchedUpdateWithResultHandling } = BatchedUpdateModule
  9. const WRITE_CONCURRENCY = parseInt(process.env.WRITE_CONCURRENCY, 10) || 10
  10. const mixpanelSinkQueue = getQueue('analytics-mixpanel-sink')
  11. async function processUser(user) {
  12. const analyticsId = user.analyticsId || user._id
  13. await _sendPropertyToQueue(analyticsId, 'user-id', user._id)
  14. await _sendPropertyToQueue(analyticsId, 'analytics-id', analyticsId)
  15. await _sendPropertyToQueue(analyticsId, 'created-at', user.signUpDate)
  16. if (user.alphaProgram !== undefined) {
  17. await _sendPropertyToQueue(analyticsId, 'alpha-program', user.alphaProgram)
  18. }
  19. if (user.betaProgram !== undefined) {
  20. await _sendPropertyToQueue(analyticsId, 'beta-program', user.betaProgram)
  21. }
  22. const groupSubscriptionPlanCode = await _getGroupSubscriptionPlanCode(
  23. user._id
  24. )
  25. if (groupSubscriptionPlanCode) {
  26. await _sendPropertyToQueue(
  27. analyticsId,
  28. 'group-subscription-plan-code',
  29. groupSubscriptionPlanCode
  30. )
  31. }
  32. const matchedFeatureSet = FeaturesHelper.getMatchedFeatureSet(user.features)
  33. if (matchedFeatureSet !== 'personal') {
  34. await _sendPropertyToQueue(analyticsId, 'feature-set', matchedFeatureSet)
  35. }
  36. if (user.splitTests) {
  37. for (const splitTestName of Object.keys(user.splitTests)) {
  38. const assignments = user.splitTests[splitTestName]
  39. if (Array.isArray(assignments)) {
  40. for (const assignment of assignments) {
  41. await _sendPropertyToQueue(
  42. analyticsId,
  43. `split-test-${splitTestName}-${assignment.versionNumber}`,
  44. `${assignment.variantName}`
  45. )
  46. }
  47. }
  48. }
  49. }
  50. }
  51. async function _getGroupSubscriptionPlanCode(userId) {
  52. const subscriptions =
  53. await SubscriptionLocator.promises.getMemberSubscriptions(userId)
  54. let bestPlanCode = null
  55. let bestFeatures = {}
  56. for (const subscription of subscriptions) {
  57. const plan = PlansLocator.findLocalPlanInSettings(subscription.planCode)
  58. if (
  59. plan &&
  60. FeaturesHelper.isFeatureSetBetter(plan.features, bestFeatures)
  61. ) {
  62. bestPlanCode = plan.planCode
  63. bestFeatures = plan.features
  64. }
  65. }
  66. return bestPlanCode
  67. }
  68. async function _sendPropertyToQueue(
  69. analyticsId,
  70. propertyName,
  71. propertyValue,
  72. createdAt = new Date()
  73. ) {
  74. if (propertyValue == null) {
  75. return
  76. }
  77. await mixpanelSinkQueue.add('user-property', {
  78. analyticsId,
  79. propertyName,
  80. propertyValue,
  81. createdAt,
  82. })
  83. }
  84. async function processBatch(_, users) {
  85. await promiseMapWithLimit(WRITE_CONCURRENCY, users, async user => {
  86. await processUser(user)
  87. })
  88. }
  89. batchedUpdateWithResultHandling(
  90. 'users',
  91. {
  92. $nor: [
  93. { thirdPartyIdentifiers: { $exists: false } },
  94. { thirdPartyIdentifiers: { $size: 0 } },
  95. ],
  96. },
  97. processBatch,
  98. {
  99. _id: true,
  100. analyticsId: true,
  101. signUpDate: true,
  102. splitTests: true,
  103. alphaProgram: true,
  104. betaProgram: true,
  105. }
  106. )