export_institution_chat.mjs 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. import {
  2. db,
  3. READ_PREFERENCE_SECONDARY,
  4. ObjectId,
  5. } from '../app/src/infrastructure/mongodb.js'
  6. import minimist from 'minimist'
  7. import InstitutionHubsController from '../modules/metrics/app/src/InstitutionHubsController.mjs'
  8. function usage() {
  9. console.log(
  10. `Usage: node export_institution_chat.js --institution <id> --from <date> --to <date> [--pretty] [--help]
  11. --institution=ID
  12. The V1 institution ID
  13. --from=DATE
  14. The start of the report period. Specified as an ISO 8601 date string
  15. (e.g. 2024-08-01T00:00:00.000Z)
  16. --to=DATE
  17. The end of the report period. Specified as an ISO 8601 date string
  18. (e.g. 2024-09-01T00:00:00.000Z)
  19. --help
  20. Prints this help page\n`
  21. )
  22. }
  23. function parseArgs() {
  24. const argv = minimist(process.argv.slice(2), {
  25. string: ['institution', 'from', 'to'],
  26. bool: ['help'],
  27. default: {
  28. help: false,
  29. },
  30. })
  31. if (argv.help) {
  32. usage()
  33. process.exit(0)
  34. }
  35. if (!argv.institution || !argv.from || !argv.to) {
  36. usage()
  37. process.exit(1)
  38. }
  39. const institutionId = parseInt(argv.institution, 10)
  40. const from = new Date(argv.from).getTime()
  41. const to = new Date(argv.to).getTime()
  42. if (to < from) {
  43. console.error('The end date must be after the start date.')
  44. process.exit(1)
  45. }
  46. return { institutionId, from, to }
  47. }
  48. async function fetchInstitutionAndAffiliations(institutionId) {
  49. const { json: affiliations } =
  50. await InstitutionHubsController.promises.v1InstitutionsApi(
  51. institutionId,
  52. 'csv_affiliations'
  53. )
  54. return affiliations.filter(({ license }) => license === 'pro_plus')
  55. }
  56. function getUserMappings(affiliations) {
  57. const entries = affiliations.map(({ user_id: userId, email }) => [
  58. userId,
  59. email,
  60. ])
  61. return new Map(entries)
  62. }
  63. async function main() {
  64. const args = parseArgs()
  65. const affiliations = await fetchInstitutionAndAffiliations(args.institutionId)
  66. const userMappings = getUserMappings(affiliations)
  67. const projectRecords = []
  68. for (const [userId, email] of userMappings.entries()) {
  69. projectRecords.push(...(await processUser(userId, email, args)))
  70. }
  71. console.log(JSON.stringify(projectRecords, null, 2))
  72. }
  73. async function processUser(userId, email, args) {
  74. const projectsOwnedByUser = db.projects.find(
  75. { owner_ref: new ObjectId(userId) },
  76. { projection: { name: 1 }, readPreference: READ_PREFERENCE_SECONDARY }
  77. )
  78. const projectRecords = []
  79. for await (const project of projectsOwnedByUser) {
  80. const hasMessages = await processProject(project, args)
  81. if (hasMessages) {
  82. projectRecords.push({
  83. projectId: project._id,
  84. owner: email,
  85. })
  86. }
  87. }
  88. return projectRecords
  89. }
  90. async function processProject(project, args) {
  91. const { _id: projectId } = project
  92. const globalRoom = await db.rooms.findOne(
  93. {
  94. project_id: new ObjectId(projectId),
  95. thread_id: { $exists: false },
  96. },
  97. { readPreference: READ_PREFERENCE_SECONDARY }
  98. )
  99. if (!globalRoom) {
  100. return null
  101. }
  102. const messages = await db.messages
  103. .find(
  104. {
  105. room_id: globalRoom._id,
  106. timestamp: { $gte: args.from, $lte: args.to },
  107. },
  108. {
  109. projection: {
  110. user_id: 1,
  111. timestamp: 1,
  112. },
  113. readPreference: READ_PREFERENCE_SECONDARY,
  114. }
  115. )
  116. .sort({ timestamp: 1 })
  117. .toArray()
  118. return messages.length > 0
  119. }
  120. try {
  121. await main()
  122. process.exit(0)
  123. } catch (err) {
  124. console.error(err)
  125. process.exit(1)
  126. }