add_user_count_to_csv.js 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. // Usage: node scripts/add_user_count_to_csv.js [OPTS] [INPUT-FILE]
  2. // Looks up the number of users for each domain in the input csv file and adds
  3. // columns for the number of users in the domain, subdomains, and total.
  4. const fs = require('fs')
  5. const csv = require('csv/sync')
  6. const minimist = require('minimist')
  7. const UserGetter = require('../app/src/Features/User/UserGetter')
  8. const { db, waitForDb } = require('../app/src/infrastructure/mongodb')
  9. const _ = require('lodash')
  10. const argv = minimist(process.argv.slice(2), {
  11. string: ['domain', 'output'],
  12. boolean: ['help'],
  13. alias: {
  14. domain: 'd',
  15. output: 'o',
  16. },
  17. default: {
  18. domain: 'Email domain',
  19. output: '/dev/stdout',
  20. },
  21. })
  22. if (argv.help || argv._.length > 1) {
  23. console.error(`Usage: node scripts/add_user_count_to_csv.js [OPTS] [INPUT-FILE]
  24. Looks up the number of users for each domain in the input file and adds
  25. columns for the number of users in the domain, subdomains, and total.
  26. Options:
  27. --domain name of the csv column containing the email domain (default: "Email domain")
  28. --output output file (default: /dev/stdout)
  29. `)
  30. process.exit(1)
  31. }
  32. const input = fs.readFileSync(argv._[0], 'utf8')
  33. const records = csv.parse(input, { columns: true })
  34. if (records.length === 0) {
  35. console.error('No records in input file')
  36. process.exit(1)
  37. }
  38. async function main() {
  39. await waitForDb()
  40. for (const record of records) {
  41. const domain = record[argv.domain]
  42. const { domainUserCount, subdomainUserCount } = await getUserCount(domain, {
  43. _id: 1,
  44. })
  45. record['Domain Users'] = domainUserCount
  46. record['Subdomain Users'] = subdomainUserCount
  47. record['Total Users'] = domainUserCount + subdomainUserCount
  48. }
  49. const output = csv.stringify(records, { header: true })
  50. fs.writeFileSync(argv.output, output)
  51. }
  52. async function getUserCount(domain) {
  53. const domainUsers = await UserGetter.promises.getUsersByHostname(domain, {
  54. _id: 1,
  55. })
  56. const subdomainUsers = await getUsersByHostnameWithSubdomain(domain, {
  57. _id: 1,
  58. })
  59. return {
  60. domainUserCount: domainUsers.length,
  61. subdomainUserCount: subdomainUsers.length,
  62. }
  63. }
  64. async function getUsersByHostnameWithSubdomain(domain, projection) {
  65. const reversedDomain = domain.trim().split('').reverse().join('')
  66. const reversedDomainRegex = _.escapeRegExp(reversedDomain)
  67. const query = {
  68. emails: { $exists: true },
  69. // look for users in subdomains of a domain, but not the domain itself
  70. // e.g. for domain 'foo.edu', match 'cs.foo.edu' but not 'foo.edu'
  71. // we use the reversed hostname index to do this efficiently
  72. // we need to escape the domain name to prevent '.' from matching any character
  73. 'emails.reversedHostname': { $regex: '^' + reversedDomainRegex + '\\.' },
  74. }
  75. return await db.users.find(query, { projection }).toArray()
  76. }
  77. main()
  78. .then(() => {
  79. console.error('Done')
  80. process.exit(0)
  81. })
  82. .catch(err => {
  83. console.error(err)
  84. process.exit(1)
  85. })