Skip to content

Commit

Permalink
Add "Export data to GitHub" menu logic (#211)
Browse files Browse the repository at this point in the history
The logic actually exports the grid, meaning the room, day and slot associated
with each session.
  • Loading branch information
tidoust authored Feb 13, 2025
1 parent 2394a72 commit 94ce7fa
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 54 deletions.
89 changes: 88 additions & 1 deletion tools/appscript/export-to-github.mjs
Original file line number Diff line number Diff line change
@@ -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) {
Expand All @@ -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(`
<p>Sorry, I did not export anything because I detected a new
session in the GitHub repository that is not in the spreadsheet yet:
</p>
<blockquote>
<p>${session.title}
(<a href="https://github.com/${session.repository}/issues/${session.number}">#${session.number}</a>)
</p>
</blockquote>
<p>Please run <b>TPAC > Sync with GitHub > Import data from GitHub</b>
first to retrieve it.
You may also want to re-validate the grid afterwards!</p>`
)
.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} (<a href="https://github.com/${s.repository}/issues/${s.number}">#${s.number}</a>)`);
const htmlOutput = HtmlService
.createHtmlOutput(`
<p>The following session${list.length > 1 ? 's were' : ' was'} updated:</p>
<ul>
<li>${list.join('</li><li>')}</li>
</ul>`
)
.setWidth(400)
.setHeight(400);
SpreadsheetApp.getUi().showModalDialog(htmlOutput, 'Updated');
}
else {
const htmlOutput = HtmlService
.createHtmlOutput(`
<p>Data seems up-to-date already, nothing to export!</p>`
)
.setWidth(400)
.setHeight(400);
SpreadsheetApp.getUi().showModalDialog(htmlOutput, 'Nothing to update');
}
}
3 changes: 1 addition & 2 deletions tools/appscript/project.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
49 changes: 48 additions & 1 deletion tools/common/project.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -576,4 +576,51 @@ export function convertProjectToJSON(project) {
return simplified;
});
return data;
}
}


/**
* 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]}"`);
}
}
}
47 changes: 0 additions & 47 deletions tools/node/lib/project.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
2 changes: 1 addition & 1 deletion tools/node/schedule.mjs
Original file line number Diff line number Diff line change
@@ -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';
Expand Down
2 changes: 1 addition & 1 deletion tools/node/try-changes.mjs
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion tools/node/view-registrants.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down

0 comments on commit 94ce7fa

Please sign in to comment.