remove_unwanted_ieee_collabratec_users.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. const path = require('path')
  2. const fs = require('fs')
  3. const AnalyticsManager = require('../app/src/Features/Analytics/AnalyticsManager')
  4. const { waitForDb } = require('../app/src/infrastructure/mongodb')
  5. const { Subscription } = require('../app/src/models/Subscription')
  6. const minimist = require('minimist')
  7. const { db } = require('../app/src/infrastructure/mongodb')
  8. const { promiseMapWithLimit } = require('@overleaf/promise-utils')
  9. const _ = require('lodash')
  10. /**
  11. * This script is used to remove some users from the IEEECollabratec group.
  12. *
  13. * Parameters:
  14. * --filename: the filename of the JSON file containing emails of users that
  15. * should **not** be removed
  16. * --commit: if present, the script will commit the changes to the database.
  17. *
  18. * Usage:
  19. * - dry run:
  20. * node scripts/remove_unwanted_ieee_collabratec_users.js --filename=emails-to-keep.json
  21. * - commit:
  22. * node scripts/remove_unwanted_ieee_collabratec_users.js --filename=emails-to-keep.json --commit
  23. */
  24. let COMMIT = false
  25. let EMAILS_FILENAME
  26. /**
  27. * The IEEE have provided us with a list of active users that should not be removed
  28. * This method retrieves those users.
  29. */
  30. function getActiveUserEmails(filename) {
  31. const data = fs.readFileSync(path.join(__dirname, filename), 'utf8')
  32. const emailsArray = JSON.parse(data)
  33. const emailsSet = new Set(emailsArray)
  34. console.log(
  35. `Read ${emailsSet.size} (${emailsArray.length} in array) emails from ${filename}`
  36. )
  37. return emailsSet
  38. }
  39. async function getIEEEUsers() {
  40. const results = await db.subscriptions
  41. .aggregate([
  42. { $match: { teamName: 'IEEECollabratec' } },
  43. { $unwind: '$member_ids' },
  44. {
  45. $lookup: {
  46. from: 'users',
  47. localField: 'member_ids',
  48. foreignField: '_id',
  49. as: 'member_details',
  50. },
  51. },
  52. {
  53. $project: {
  54. _id: 1,
  55. teamName: 1,
  56. 'member_details._id': 1,
  57. 'member_details.email': 1,
  58. 'member_details.emails.email': 1,
  59. },
  60. },
  61. ])
  62. .toArray()
  63. return results
  64. .map(subscription => subscription.member_details[0])
  65. .filter(Boolean)
  66. }
  67. async function main() {
  68. const start = performance.now()
  69. if (!EMAILS_FILENAME) {
  70. throw new Error('No email filename provided')
  71. }
  72. await waitForDb()
  73. const subscription = await Subscription.findOne({
  74. teamName: 'IEEECollabratec',
  75. })
  76. if (!subscription) {
  77. console.error(`No IEEECollabratec group subscription found so quitting`)
  78. return
  79. }
  80. /**
  81. * @type {string[]}
  82. */
  83. const oldMemberIds = subscription.member_ids.map(id => id.toString())
  84. console.log(
  85. `Found ${oldMemberIds.length} members_ids in IEEECollabratec group`
  86. )
  87. const usersArray = await getIEEEUsers()
  88. console.log(
  89. `Found ${usersArray.length} users in IEEECollabratec group. (${oldMemberIds.length - usersArray.length} missing)`
  90. )
  91. const activeUsers = getActiveUserEmails(EMAILS_FILENAME)
  92. const activeUsersFound = new Set()
  93. /**
  94. * @type {string[]}
  95. */
  96. const memberIdsToRemove = []
  97. let index = 0
  98. // Then go through each collabratec user to see if we need to remove them
  99. await promiseMapWithLimit(10, usersArray, async userDetails => {
  100. if (index % 1000 === 0)
  101. console.log(
  102. `progress: ${index} / ${usersArray.length} (${memberIdsToRemove.length} to remove)`
  103. )
  104. index = index + 1
  105. if (COMMIT) {
  106. await AnalyticsManager.setUserPropertyForUser(
  107. userDetails._id.toString(),
  108. 'ieee-retirement',
  109. true
  110. )
  111. }
  112. for (const email of userDetails.emails) {
  113. if (activeUsers.has(email.email)) {
  114. activeUsersFound.add(email.email)
  115. return
  116. }
  117. }
  118. memberIdsToRemove.push(userDetails._id.toString())
  119. })
  120. console.log(`Found ${memberIdsToRemove.length} users to remove`)
  121. /**
  122. * @type {string[]}
  123. */
  124. const memberIdsToKeep = _.difference(oldMemberIds, memberIdsToRemove)
  125. console.log(`Keeping ${memberIdsToKeep.length} users`)
  126. if (COMMIT) {
  127. await Subscription.updateOne(
  128. { teamName: 'IEEECollabratec' },
  129. { member_ids: memberIdsToKeep }
  130. )
  131. }
  132. console.log(`Found ${activeUsersFound.size} active users`)
  133. const activeUsersNotFound = Array.from(activeUsers).filter(
  134. user => !activeUsersFound.has(user)
  135. )
  136. console.log(`${activeUsersNotFound.length} IEEE active users not found:`)
  137. console.log(activeUsersNotFound)
  138. const subscriptionAfter = await Subscription.findOne({
  139. teamName: 'IEEECollabratec',
  140. })
  141. console.log(
  142. `There are now ${subscriptionAfter?.member_ids?.length} members_ids in IEEECollabratec group`
  143. )
  144. const end = performance.now()
  145. console.log(`Took ${end - start} ms`)
  146. }
  147. const setup = () => {
  148. const argv = minimist(process.argv.slice(2))
  149. COMMIT = argv.commit !== undefined
  150. EMAILS_FILENAME = argv.filename
  151. if (!COMMIT) {
  152. console.warn('Doing dry run. Add --commit to commit changes')
  153. }
  154. }
  155. setup()
  156. main()
  157. .then(() => {
  158. process.exit(0)
  159. })
  160. .catch(err => {
  161. console.error(err)
  162. process.exit(1)
  163. })