Skip to content

Commit

Permalink
list-chairs: "js" option to create a script for registrants page (#172)
Browse files Browse the repository at this point in the history
Passing `js` as second argument to list-chairs will make it spit out a
JavaScript script that contains the list of chairs but also a function to
check the list against a page that lists registrants. The script needs to
be a copied and pasted into the dev console linked to the registrants page.
It returns the list of session chairs that still need to register.
  • Loading branch information
tidoust authored Sep 16, 2024
1 parent d96156a commit 5c7bac7
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 32 deletions.
76 changes: 76 additions & 0 deletions tools/lib/check-registrants.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/**
* Function that is supposed to run within the context of a TPAC registrants
* page. Serialized by "list-chairs".
*/
export default function checkRegistrants(chairs) {
function matchChair(txt, chair) {
return txt.includes(chair.name) || txt.includes(chair.email);
}

// Extract the list of in-person registrants
const list = new Set();
const listStartEl = document.querySelector('h2[id^=meeting]');
const remoteHeadingEl = document.getElementById('remotes');
let listEl = listStartEl;
while (listEl && listEl !== remoteHeadingEl) {
if (listEl.nodeName === 'UL') {
const items = [...document.querySelectorAll('li')].map(el =>
el.textContent.trim().replace(/\s+/g, ' ').split(/:\s*attending/)[0]);
for (const item of items) {
list.add(item);
}
}
listEl = listEl.nextElementSibling;
}
const inperson = [...list];

// Extract the list of remote registrants
let remoteEl = remoteHeadingEl;
while (remoteEl.nodeName !== 'UL') {
remoteEl = remoteEl.nextElementSibling;
}
const remote = [...remoteEl.querySelectorAll('li')]
.map(el => el.textContent.trim().replace(/\s+/g, ' '));

// Compute the list of chairs that need to register
const needRegistration = chairs.filter(chair =>
!inperson.find(line => matchChair(line, chair)) &&
!remote.find(line => matchChair(line, chair)));

// Compute the list of sessions that have remote-only chairs
const sessions = chairs
.map(chair => chair.sessions)
.filter((session, idx, arr) => arr.indexOf(s => s.number === session.number) === idx);
const remoteSessions = sessions.filter(session =>
session.chairs.every(chair => remote.find(line => matchChair(line, chair)))
);

let res = [];
if (remoteSessions.length > 0) {
res.push('Sessions that have remote-only chairs:');
for (const session of remoteSessions) {
res.push(`- #${session.number}: ${session.title} - ${session.chairs.map(c => c.name).join(', ')}`);
}
res.push('');
}

const mainChairs = needRegistration.filter(chair => chair.sessions.find(s =>
s.chairs[0].name === chair.name));
if (mainChairs.length > 0) {
res.push('Chairs who proposed a session and still need to register:');
for (const chair of mainChairs) {
res.push(`- ${chair.name} <${chair.email}> - ${chair.sessions.map(s => '#' + s.number).join(', ')}`);
}
res.push('');
}

const secondary = needRegistration.filter(chair => !mainChairs.find(c => c.name === chair.name))
if (secondary.length > 0) {
res.push('Additional chairs who still need to register:');
for (const chair of secondary) {
res.push(`- ${chair.name} <${chair.email}> - ${chair.sessions.map(s => '#' + s.number).join(', ')}`);
}
}

return res.join('\n');
}
90 changes: 58 additions & 32 deletions tools/list-chairs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,30 @@ import { getEnvKey } from './lib/envkeys.mjs';
import { fetchProject } from './lib/project.mjs'
import { validateGrid } from './lib/validate.mjs';
import { authenticate } from './lib/calendar.mjs';
import checkRegistrants from './lib/check-registrants.mjs';
import puppeteer from 'puppeteer';

function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms, 'slept'));
}

async function main() {
async function main(format) {
format = format ?? 'text';

const PROJECT_OWNER = await getEnvKey('PROJECT_OWNER', 'w3c');
const PROJECT_NUMBER = await getEnvKey('PROJECT_NUMBER');
const W3CID_MAP = await getEnvKey('W3CID_MAP', {}, true);
const W3C_LOGIN = await getEnvKey('W3C_LOGIN');
const W3C_PASSWORD = await getEnvKey('W3C_PASSWORD');
console.log();
console.log(`Retrieve project ${PROJECT_OWNER}/${PROJECT_NUMBER}...`);
console.warn();
console.warn(`Retrieve project ${PROJECT_OWNER}/${PROJECT_NUMBER}...`);
const project = await fetchProject(PROJECT_OWNER, PROJECT_NUMBER);
if (!project) {
throw new Error(`Project ${PROJECT_OWNER}/${PROJECT_NUMBER} could not be retrieved`);
}
project.w3cIds = W3CID_MAP;
const { errors } = await validateGrid(project)
console.log(`Retrieve project ${PROJECT_OWNER}/${PROJECT_NUMBER}... done`);
console.warn(`Retrieve project ${PROJECT_OWNER}/${PROJECT_NUMBER}... done`);

const sessions = project.sessions.filter(session => session.chairs);
sessions.sort((s1, s2) => s1.number - s2.number);
Expand Down Expand Up @@ -60,8 +63,8 @@ async function main() {
}

if (W3C_LOGIN && W3C_PASSWORD) {
console.log();
console.log('Retrieving chair emails...');
console.warn();
console.warn('Retrieving chair emails...');
const browser = await puppeteer.launch({ headless: true });
try {
for (const chair of chairs) {
Expand All @@ -81,46 +84,69 @@ async function main() {
finally {
page.close();
}
console.warn('Wait 2s to ease load on server...');
await sleep(2000);
console.warn('Wait 2s to ease load on server... done');
console.warn('Wait 1s to ease load on server...');
await sleep(1000);
console.warn('Wait 1s to ease load on server... done');
}
}
finally {
browser.close();
}
console.log('Retrieving chair emails... done');
console.warn('Retrieving chair emails... done');
}

console.log();
console.log('All chairs');
console.log('----------');
for (const chair of chairs) {
console.log(formatChair(chair));
if (format === 'json' || format === 'js') {
const copy = chairs.map(chair => {
return Object.assign({}, chair, {
sessions: sessions
.filter(s => s.chairs.find(c => c.name === chair.name))
.map(s => {
return { number: s.number, title: s.title, chairs: s.chairs };
})
});
});
if (format === 'json') {
console.log(JSON.stringify(copy, null, 2));
}
else {
console.log('const chairs = ' + JSON.stringify(copy, null, 2) + ';');
console.log();
console.log(checkRegistrants.toString());
console.log();
console.log('console.log(checkRegistrants(chairs));');
}
}

console.log();
console.log('All emails');
console.log('----------');
const emails = chairs
.filter(chair => chair.email)
.map(chair => `${chair.name} <${chair.email}>`)
console.log(emails.join(', '));

console.log();
console.log('Per session');
console.log('-----------');
for (const session of sessions) {
console.log(`#${session.number} - ${session.title}`);
for (const chair of session.chairs) {
else {
console.log();
console.log('All chairs');
console.log('----------');
for (const chair of chairs) {
console.log(formatChair(chair));
}

console.log();
console.log('All emails');
console.log('----------');
const emails = chairs
.filter(chair => chair.email)
.map(chair => `${chair.name} <${chair.email}>`)
console.log(emails.join(', '));

console.log();
console.log('Per session');
console.log('-----------');
for (const session of sessions) {
console.log(`#${session.number} - ${session.title}`);
for (const chair of session.chairs) {
console.log(formatChair(chair));
}
console.log();
}
}
}

main()
main(process.argv[2])
.catch(err => {
console.log(`Something went wrong: ${err.message}`);
console.error(`Something went wrong: ${err.message}`);
throw err;
});

0 comments on commit 5c7bac7

Please sign in to comment.