From d2e6608f01329c76995d80d19e7ca560d6e45a7e Mon Sep 17 00:00:00 2001 From: Andrew Rumble Date: Fri, 11 Oct 2024 15:33:14 +0100 Subject: [PATCH] Convert export_institution_chat script to ES module GitOrigin-RevId: 8570922654049edb514f8091421e5be2f04988cd --- .../web/scripts/export_institution_chat.mjs | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 services/web/scripts/export_institution_chat.mjs diff --git a/services/web/scripts/export_institution_chat.mjs b/services/web/scripts/export_institution_chat.mjs new file mode 100644 index 0000000000..97d6e32508 --- /dev/null +++ b/services/web/scripts/export_institution_chat.mjs @@ -0,0 +1,146 @@ +import { + db, + READ_PREFERENCE_SECONDARY, + waitForDb, + ObjectId, +} from '../app/src/infrastructure/mongodb.js' +import minimist from 'minimist' +import InstitutionHubsController from '../modules/metrics/app/src/InstitutionHubsController.js' + +function usage() { + console.log( + `Usage: node export_institution_chat.js --institution --from --to [--pretty] [--help] + + --institution=ID + The V1 institution ID + + --from=DATE + The start of the report period. Specified as an ISO 8601 date string + (e.g. 2024-08-01T00:00:00.000Z) + + --to=DATE + The end of the report period. Specified as an ISO 8601 date string + (e.g. 2024-09-01T00:00:00.000Z) + + --help + Prints this help page\n` + ) +} + +function parseArgs() { + const argv = minimist(process.argv.slice(2), { + string: ['institution', 'from', 'to'], + bool: ['help'], + default: { + help: false, + }, + }) + if (argv.help) { + usage() + process.exit(0) + } + + if (!argv.institution || !argv.from || !argv.to) { + usage() + process.exit(1) + } + + const institutionId = parseInt(argv.institution, 10) + const from = new Date(argv.from).getTime() + const to = new Date(argv.to).getTime() + + if (to < from) { + console.error('The end date must be after the start date.') + process.exit(1) + } + + return { institutionId, from, to } +} + +async function fetchInstitutionAndAffiliations(institutionId) { + const { json: affiliations } = + await InstitutionHubsController.promises.v1InstitutionsApi( + institutionId, + 'csv_affiliations' + ) + return affiliations.filter(({ license }) => license === 'pro_plus') +} + +function getUserMappings(affiliations) { + const entries = affiliations.map(({ user_id: userId, email }) => [ + userId, + email, + ]) + return new Map(entries) +} + +async function main() { + const args = parseArgs() + await waitForDb() + const affiliations = await fetchInstitutionAndAffiliations(args.institutionId) + const userMappings = getUserMappings(affiliations) + + const projectRecords = [] + + for (const [userId, email] of userMappings.entries()) { + projectRecords.push(...(await processUser(userId, email, args))) + } + console.log(JSON.stringify(projectRecords, null, 2)) +} + +async function processUser(userId, email, args) { + const projectsOwnedByUser = db.projects.find( + { owner_ref: new ObjectId(userId) }, + { projection: { name: 1 }, readPreference: READ_PREFERENCE_SECONDARY } + ) + const projectRecords = [] + for await (const project of projectsOwnedByUser) { + const hasMessages = await processProject(project, args) + if (hasMessages) { + projectRecords.push({ + projectId: project._id, + owner: email, + }) + } + } + return projectRecords +} + +async function processProject(project, args) { + const { _id: projectId } = project + const globalRoom = await db.rooms.findOne( + { + project_id: new ObjectId(projectId), + thread_id: { $exists: false }, + }, + { readPreference: READ_PREFERENCE_SECONDARY } + ) + if (!globalRoom) { + return null + } + const messages = await db.messages + .find( + { + room_id: globalRoom._id, + timestamp: { $gte: args.from, $lte: args.to }, + }, + { + projection: { + user_id: 1, + timestamp: 1, + }, + readPreference: READ_PREFERENCE_SECONDARY, + } + ) + .sort({ timestamp: 1 }) + .toArray() + return messages.length > 0 +} + +try { + await main() + process.exit(0) +} catch (err) { + console.error(err) + process.exit(1) +}