diff --git a/tools/appscript/export-to-github.mjs b/tools/appscript/export-to-github.mjs index 7b0db32..3510cc9 100644 --- a/tools/appscript/export-to-github.mjs +++ b/tools/appscript/export-to-github.mjs @@ -1,10 +1,13 @@ import { getProject } from './project.mjs'; import reportError from './report-error.mjs'; +import { fetchProjectFromGitHub, saveSessionMeetings } from '../common/project.mjs'; + /** * Trigger a GitHub workflow that refreshes the data from GitHub */ -export default function () { +export default async function () { + // TODO: consider reading only the list of sessions const project = getProject(SpreadsheetApp.getActiveSpreadsheet()); if (!project.metadata.reponame) { @@ -21,5 +24,89 @@ If not, ask François or Ian to run the required initialization steps.`); owner: repoparts.length > 1 ? repoparts[0] : 'w3c', name: repoparts.length > 1 ? repoparts[1] : repoparts[0] }; + + let githubProject; + try { + githubProject = await fetchProjectFromGitHub( + repo.owner === 'w3c' ? repo.owner : `user/${repo.owner}`, + repo.name, + null + ); + } + catch (err) { + reportError(err.toString()); + return; + } + + const updated = []; + try { + // Check sessions that need an update + for (const session of githubProject.sessions) { + const ssSession = project.sessions.find(s => + s.number === session.number); + if (!ssSession) { + const htmlOutput = HtmlService + .createHtmlOutput(` +
Sorry, I did not export anything because I detected a new + session in the GitHub repository that is not in the spreadsheet yet: +
+++${session.title} + (#${session.number}) +
+
Please run TPAC > Sync with GitHub > Import data from GitHub + first to retrieve it. + You may also want to re-validate the grid afterwards!
` + ) + .setWidth(400) + .setHeight(400); + SpreadsheetApp.getUi().showModalDialog(htmlOutput, 'Not up-to-date!'); + return; + } + + // TODO: handle meeting column for TPAC group meetings + + if ((session.room !== ssSession.room) || + (session.day !== ssSession.day) || + (session.slot !== ssSession.slot)) { + console.warn(`- updating #${session.number}...`); + session.room = ssSession.room; + session.day = ssSession.day; + session.slot = ssSession.slot; + await saveSessionMeetings(session, githubProject); + updated.push(session); + console.warn(`- updating #${session.number}... done`); + } + } + } + catch(err) { + reportError(err.toString()); + return; + } + // Report the list of sessions that were updated + if (updated.length > 0) { + const list = updated.map(s => + `${s.title} (#${s.number})`); + const htmlOutput = HtmlService + .createHtmlOutput(` +The following session${list.length > 1 ? 's were' : ' was'} updated:
+Data seems up-to-date already, nothing to export!
` + ) + .setWidth(400) + .setHeight(400); + SpreadsheetApp.getUi().showModalDialog(htmlOutput, 'Nothing to update'); + } } \ No newline at end of file diff --git a/tools/appscript/project.mjs b/tools/appscript/project.mjs index a4bef32..f33a1d9 100644 --- a/tools/appscript/project.mjs +++ b/tools/appscript/project.mjs @@ -212,9 +212,8 @@ export function getProject(spreadsheet) { // TODO: how to retrieve the labels? labels: [], - // TODO: initialize from sessions sheet if it exists // TODO: complete with meetings sheet if it exists - sessions: [] + sessions: sheets.sessions.values }; return project; diff --git a/tools/common/project.mjs b/tools/common/project.mjs index 4872e6e..264ded3 100644 --- a/tools/common/project.mjs +++ b/tools/common/project.mjs @@ -576,4 +576,51 @@ export function convertProjectToJSON(project) { return simplified; }); return data; -} \ No newline at end of file +} + + +/** + * Record the meetings assignments for the provided session + */ +export async function saveSessionMeetings(session, project, options) { + // Project may not have some of the custom fields, and we may only + // be interested in a restricted set of them + const fields = ['room', 'day', 'slot', 'meeting', 'trymeout', 'registrants'] + .filter(field => project[field + 'sFieldId']) + .filter(field => !options || !options.fields || options.fields.includes(field)); + for (const field of fields) { + const prop = ['meeting', 'trymeout', 'registrants'].includes(field) ? + 'text': 'singleSelectOptionId'; + let value = null; + if (prop === 'text') { + // Text field + if (session[field]) { + value = `"${session[field]}"`; + } + } + else { + // Option in a selection field + const obj = project[field + 's'].find(o => o.name === session[field]); + if (obj) { + value = `"${obj.id}"`; + } + } + const resField = await sendGraphQLRequest(`mutation { + updateProjectV2ItemFieldValue(input: { + clientMutationId: "mutatis mutandis", + fieldId: "${project[field + 'sFieldId']}", + itemId: "${session.projectItemId}", + projectId: "${project.id}", + value: { + ${prop}: ${value} + } + }) { + clientMutationId + } + }`); + if (!resField?.data?.updateProjectV2ItemFieldValue?.clientMutationId) { + console.log(JSON.stringify(resField, null, 2)); + throw new Error(`GraphQL error, could not assign session #${session.number} to ${field} value "${session[field]}"`); + } + } +} diff --git a/tools/node/lib/project.mjs b/tools/node/lib/project.mjs index c2f8129..6663226 100644 --- a/tools/node/lib/project.mjs +++ b/tools/node/lib/project.mjs @@ -76,53 +76,6 @@ export async function fetchProject(login, id) { } -/** - * Record the meetings assignments for the provided session - */ -export async function saveSessionMeetings(session, project, options) { - // Project may not have some of the custom fields, and we may only - // be interested in a restricted set of them - const fields = ['room', 'day', 'slot', 'meeting', 'trymeout', 'registrants'] - .filter(field => project[field + 'sFieldId']) - .filter(field => !options || !options.fields || options.fields.includes(field)); - for (const field of fields) { - const prop = ['meeting', 'trymeout', 'registrants'].includes(field) ? - 'text': 'singleSelectOptionId'; - let value = null; - if (prop === 'text') { - // Text field - if (session[field]) { - value = `"${session[field]}"`; - } - } - else { - // Option in a selection field - const obj = project[field + 's'].find(o => o.name === session[field]); - if (obj) { - value = `"${obj.id}"`; - } - } - const resField = await sendGraphQLRequest(`mutation { - updateProjectV2ItemFieldValue(input: { - clientMutationId: "mutatis mutandis", - fieldId: "${project[field + 'sFieldId']}", - itemId: "${session.projectItemId}", - projectId: "${project.id}", - value: { - ${prop}: ${value} - } - }) { - clientMutationId - } - }`); - if (!resField?.data?.updateProjectV2ItemFieldValue?.clientMutationId) { - console.log(JSON.stringify(resField, null, 2)); - throw new Error(`GraphQL error, could not assign session #${session.number} to ${field} value "${session[field]}"`); - } - } -} - - /** * Record session validation problems */ diff --git a/tools/node/schedule.mjs b/tools/node/schedule.mjs index 4bff47e..ab0a02c 100644 --- a/tools/node/schedule.mjs +++ b/tools/node/schedule.mjs @@ -1,6 +1,6 @@ import { readFile } from 'fs/promises'; import { convertProjectToHTML } from './lib/project2html.mjs'; -import { saveSessionMeetings } from './lib/project.mjs'; +import { saveSessionMeetings } from '../common/project.mjs'; import { parseMeetingsChanges, applyMeetingsChanges } from './lib/meetings.mjs'; import { validateGrid } from '../common/validate.mjs'; import { suggestSchedule } from '../common/schedule.mjs'; diff --git a/tools/node/try-changes.mjs b/tools/node/try-changes.mjs index 7a19105..ea641a9 100644 --- a/tools/node/try-changes.mjs +++ b/tools/node/try-changes.mjs @@ -1,5 +1,5 @@ import { validateGrid } from '../common/validate.mjs'; -import { saveSessionMeetings } from './lib/project.mjs'; +import { saveSessionMeetings } from '../common/project.mjs'; import { convertProjectToHTML } from './lib/project2html.mjs'; export default async function (project, options) { diff --git a/tools/node/view-registrants.mjs b/tools/node/view-registrants.mjs index 9962d7c..8650a1d 100644 --- a/tools/node/view-registrants.mjs +++ b/tools/node/view-registrants.mjs @@ -2,7 +2,7 @@ import puppeteer from 'puppeteer'; import { validateSession } from '../common/validate.mjs'; import { authenticate } from './lib/calendar.mjs'; import { getEnvKey } from '../common/envkeys.mjs'; -import { saveSessionMeetings } from './lib/project.mjs'; +import { saveSessionMeetings } from '../common/project.mjs'; import { parseSessionMeetings } from '../common/meetings.mjs'; export default async function (project, number, options) {