backfill_mixpanel_user_properties.mjs 3.4 KB

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