backfill_mixpanel_user_properties.mjs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  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. /**
  14. * @param {any} user
  15. */
  16. async function processUser(user) {
  17. const analyticsId = user.analyticsId || user._id
  18. await _sendPropertyToQueue(analyticsId, 'user-id', user._id)
  19. await _sendPropertyToQueue(analyticsId, 'analytics-id', analyticsId)
  20. await _sendPropertyToQueue(analyticsId, 'created-at', user.signUpDate)
  21. if (user.alphaProgram !== undefined) {
  22. await _sendPropertyToQueue(analyticsId, 'alpha-program', user.alphaProgram)
  23. }
  24. if (user.betaProgram !== undefined) {
  25. await _sendPropertyToQueue(analyticsId, 'beta-program', user.betaProgram)
  26. }
  27. const groupSubscriptionPlanCode = await _getGroupSubscriptionPlanCode(
  28. user._id
  29. )
  30. if (groupSubscriptionPlanCode) {
  31. await _sendPropertyToQueue(
  32. analyticsId,
  33. 'group-subscription-plan-code',
  34. groupSubscriptionPlanCode
  35. )
  36. }
  37. const matchedFeatureSet = FeaturesHelper.getMatchedFeatureSet(user.features)
  38. if (matchedFeatureSet !== 'personal') {
  39. await _sendPropertyToQueue(analyticsId, 'feature-set', matchedFeatureSet)
  40. }
  41. if (user.splitTests) {
  42. for (const splitTestName of Object.keys(user.splitTests)) {
  43. const assignments = user.splitTests[splitTestName]
  44. if (Array.isArray(assignments)) {
  45. for (const assignment of assignments) {
  46. await _sendPropertyToQueue(
  47. analyticsId,
  48. `split-test-${splitTestName}-${assignment.versionNumber}`,
  49. `${assignment.variantName}`
  50. )
  51. }
  52. }
  53. }
  54. }
  55. }
  56. /**
  57. * @param {any} userId
  58. */
  59. async function _getGroupSubscriptionPlanCode(userId) {
  60. const subscriptions =
  61. await SubscriptionLocator.promises.getMemberSubscriptions(userId)
  62. let bestPlanCode = null
  63. let bestFeatures = {}
  64. for (const subscription of subscriptions) {
  65. const plan = PlansLocator.findLocalPlanInSettings(subscription.planCode)
  66. if (
  67. plan &&
  68. plan.features &&
  69. FeaturesHelper.isFeatureSetBetter(plan.features, bestFeatures)
  70. ) {
  71. bestPlanCode = plan.planCode
  72. bestFeatures = plan.features
  73. }
  74. }
  75. return bestPlanCode
  76. }
  77. /**
  78. * @param {any} analyticsId
  79. * @param {any} propertyName
  80. * @param {any} propertyValue
  81. * @param {any} [createdAt]
  82. */
  83. async function _sendPropertyToQueue(
  84. analyticsId,
  85. propertyName,
  86. propertyValue,
  87. createdAt = new Date()
  88. ) {
  89. if (propertyValue == null) {
  90. return
  91. }
  92. await mixpanelSinkQueue.add('user-property', {
  93. analyticsId,
  94. propertyName,
  95. propertyValue,
  96. createdAt,
  97. })
  98. }
  99. /**
  100. * @param {any} _
  101. * @param {any} users
  102. */
  103. async function processBatch(_, users) {
  104. await promiseMapWithLimit(WRITE_CONCURRENCY, users, async user => {
  105. await processUser(user)
  106. })
  107. }
  108. batchedUpdateWithResultHandling(
  109. db.users,
  110. {
  111. $nor: [
  112. { thirdPartyIdentifiers: { $exists: false } },
  113. { thirdPartyIdentifiers: { $size: 0 } },
  114. ],
  115. },
  116. processBatch,
  117. {
  118. _id: true,
  119. analyticsId: true,
  120. signUpDate: true,
  121. splitTests: true,
  122. alphaProgram: true,
  123. betaProgram: true,
  124. }
  125. )