const path = require('path') const fs = require('fs') const AnalyticsManager = require('../app/src/Features/Analytics/AnalyticsManager') const { waitForDb } = require('../app/src/infrastructure/mongodb') const { Subscription } = require('../app/src/models/Subscription') const minimist = require('minimist') const { db } = require('../app/src/infrastructure/mongodb') const { promiseMapWithLimit } = require('@overleaf/promise-utils') const _ = require('lodash') /** * This script is used to remove some users from the IEEECollabratec group. * * Parameters: * --filename: the filename of the JSON file containing emails of users that * should **not** be removed * --commit: if present, the script will commit the changes to the database. * * Usage: * - dry run: * node scripts/remove_unwanted_ieee_collabratec_users.js --filename=emails-to-keep.json * - commit: * node scripts/remove_unwanted_ieee_collabratec_users.js --filename=emails-to-keep.json --commit */ let COMMIT = false let EMAILS_FILENAME /** * The IEEE have provided us with a list of active users that should not be removed * This method retrieves those users. */ function getActiveUserEmails(filename) { const data = fs.readFileSync(path.join(__dirname, filename), 'utf8') const emailsArray = JSON.parse(data) const emailsSet = new Set(emailsArray) console.log( `Read ${emailsSet.size} (${emailsArray.length} in array) emails from ${filename}` ) return emailsSet } async function getIEEEUsers() { const results = await db.subscriptions .aggregate([ { $match: { teamName: 'IEEECollabratec' } }, { $unwind: '$member_ids' }, { $lookup: { from: 'users', localField: 'member_ids', foreignField: '_id', as: 'member_details', }, }, { $project: { _id: 1, teamName: 1, 'member_details._id': 1, 'member_details.email': 1, 'member_details.emails.email': 1, }, }, ]) .toArray() return results .map(subscription => subscription.member_details[0]) .filter(Boolean) } async function main() { const start = performance.now() if (!EMAILS_FILENAME) { throw new Error('No email filename provided') } await waitForDb() const subscription = await Subscription.findOne({ teamName: 'IEEECollabratec', }) if (!subscription) { console.error(`No IEEECollabratec group subscription found so quitting`) return } /** * @type {string[]} */ const oldMemberIds = subscription.member_ids.map(id => id.toString()) console.log( `Found ${oldMemberIds.length} members_ids in IEEECollabratec group` ) const usersArray = await getIEEEUsers() console.log( `Found ${usersArray.length} users in IEEECollabratec group. (${oldMemberIds.length - usersArray.length} missing)` ) const activeUsers = getActiveUserEmails(EMAILS_FILENAME) const activeUsersFound = new Set() /** * @type {string[]} */ const memberIdsToRemove = [] let index = 0 // Then go through each collabratec user to see if we need to remove them await promiseMapWithLimit(10, usersArray, async userDetails => { if (index % 1000 === 0) console.log( `progress: ${index} / ${usersArray.length} (${memberIdsToRemove.length} to remove)` ) index = index + 1 if (COMMIT) { await AnalyticsManager.setUserPropertyForUser( userDetails._id.toString(), 'ieee-retirement', true ) } for (const email of userDetails.emails) { if (activeUsers.has(email.email)) { activeUsersFound.add(email.email) return } } memberIdsToRemove.push(userDetails._id.toString()) }) console.log(`Found ${memberIdsToRemove.length} users to remove`) /** * @type {string[]} */ const memberIdsToKeep = _.difference(oldMemberIds, memberIdsToRemove) console.log(`Keeping ${memberIdsToKeep.length} users`) if (COMMIT) { await Subscription.updateOne( { teamName: 'IEEECollabratec' }, { member_ids: memberIdsToKeep } ) } console.log(`Found ${activeUsersFound.size} active users`) const activeUsersNotFound = Array.from(activeUsers).filter( user => !activeUsersFound.has(user) ) console.log(`${activeUsersNotFound.length} IEEE active users not found:`) console.log(activeUsersNotFound) const subscriptionAfter = await Subscription.findOne({ teamName: 'IEEECollabratec', }) console.log( `There are now ${subscriptionAfter?.member_ids?.length} members_ids in IEEECollabratec group` ) const end = performance.now() console.log(`Took ${end - start} ms`) } const setup = () => { const argv = minimist(process.argv.slice(2)) COMMIT = argv.commit !== undefined EMAILS_FILENAME = argv.filename if (!COMMIT) { console.warn('Doing dry run. Add --commit to commit changes') } } setup() main() .then(() => { process.exit(0) }) .catch(err => { console.error(err) process.exit(1) })