backfill_mixpanel_user_properties.mjs 3.4 KB

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