Skip to content

Commit

Permalink
New "import from GitHub" menu in a Google spreadsheet (#209)
Browse files Browse the repository at this point in the history
The import no longer kicks off a GitHub workflow but rather pulls the data from
GitHub directly and refreshes the content of the spreadsheet.

Custom data associated with rooms, days, slots, or even breakouts in additional
columns is preserved. Formatting is preserved too. The breakouts sheet is
created if needed.
  • Loading branch information
tidoust authored Feb 11, 2025
1 parent 2fc7b67 commit 0151b1d
Show file tree
Hide file tree
Showing 12 changed files with 901 additions and 460 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"test": "mocha",
"appscript-pull": "clasp pull",
"appscript-push": "clasp push",
"appscript-bundle": "rollup tools/appscript/main.mjs --file tools/appscript/bundle.js --no-treeshake",
"appscript-bundle": "rollup tools/appscript/main.mjs --file tools/appscript/bundle.js --no-treeshake --external ../../test/stubs.mjs",
"appscript": "npm run appscript-pull && npm run appscript-bundle && npm run appscript-push"
},
"engines": {
Expand Down
15 changes: 15 additions & 0 deletions test/stubs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,21 @@ export async function sendGraphQLRequest(query, acceptHeader = '') {
}
};
}
else if (query.includes('projectsV2(')) {
const type = query.includes('organization(') ? 'organization' : 'user'
const id = query.match(/repository\(name: "([^"]+)"\)/)[1];
const result = { data: {} };
result.data[type] = {
repository: {
projectsV2: {
nodes: [{
number: id
}]
}
}
};
return result;
}
else {
throw new Error('Unexpected GraphQL query request, cannot fake it!', { cause: query });
}
Expand Down
13 changes: 10 additions & 3 deletions tools/appscript/add-custom-menu.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,16 @@
*/
export default function () {
SpreadsheetApp.getUi().createMenu('TPAC')
.addItem('Export event data as JSON', 'exportEventData')
.addItem('Import data from GitHub', 'importFromGithub')
.addItem('Generate grid', 'generateGrid')
.addItem('Refresh grid', 'generateGrid')
.addItem('Validate grid', 'validateGrid')
.addSeparator()
.addSubMenu(
SpreadsheetApp.getUi()
.createMenu('Sync with GitHub')
.addItem('Import data from GitHub', 'importFromGitHub')
.addItem('Export data to GitHub', 'exportToGitHub')
.addItem('Export event data as JSON', 'exportEventData')
)
.addToUi();
}

25 changes: 25 additions & 0 deletions tools/appscript/export-to-github.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { getProject } from './project.mjs';
import reportError from './report-error.mjs';

/**
* Trigger a GitHub workflow that refreshes the data from GitHub
*/
export default function () {
const project = getProject(SpreadsheetApp.getActiveSpreadsheet());

if (!project.metadata.reponame) {
reportError(`No GitHub repository associated with the current document.
Make sure that the "GitHub repository name" parameter is set in the "Event" sheet.
Also make sure the targeted repository and project have been properly initialized.
If not, ask François or Ian to run the required initialization steps.`);
}

const repoparts = project.metadata.reponame.split('/');
const repo = {
owner: repoparts.length > 1 ? repoparts[0] : 'w3c',
name: repoparts.length > 1 ? repoparts[1] : repoparts[0]
};

}
66 changes: 36 additions & 30 deletions tools/appscript/import-from-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 } from '../common/project.mjs';
import { refreshProject } from './project.mjs';
import * as YAML from '../../node_modules/yaml/browser/index.js';

/**
* Trigger a GitHub workflow that refreshes the data from GitHub
*/
export default function () {
export default async function () {
const project = getProject(SpreadsheetApp.getActiveSpreadsheet());

if (!project.metadata.reponame) {
Expand All @@ -14,6 +17,7 @@ Make sure that the "GitHub repository name" parameter is set in the "Event" shee
Also make sure the targeted repository and project have been properly initialized.
If not, ask François or Ian to run the required initialization steps.`);
return;
}

const repoparts = project.metadata.reponame.split('/');
Expand All @@ -22,38 +26,40 @@ If not, ask François or Ian to run the required initialization steps.`);
name: repoparts.length > 1 ? repoparts[1] : repoparts[0]
};

const options = {
method : 'post',
contentType: 'application/json',
payload : JSON.stringify({
ref: 'main',
inputs: {
sheet: spreadsheet.getId()
}
}),
headers: {
'Authorization': `Bearer ${GITHUB_TOKEN}`
},
muteHttpExceptions: true
};
let githubProject;
try {
const yamlTemplateResponse = UrlFetchApp.fetch(
`https://raw.githubusercontent.com/${repo.owner}/${repo.name}/refs/heads/main/.github/ISSUE_TEMPLATE/session.yml`
);
const yamlTemplate = yamlTemplateResponse.getContentText();
const template = YAML.parse(yamlTemplate);

const response = UrlFetchApp.fetch(
`https://api.github.com/repos/${repo.owner}/${repo.name}/actions/workflows/sync-spreadsheet.yml/dispatches`,
options);
const status = response.getResponseCode();
if (status === 200 || status === 204) {
const ui = SpreadsheetApp.getUi();
ui.alert(
'Patience...',
`A job was scheduled to refresh the data in the spreadsheet. This may take a while...
The job will clear the grid in the process. Please run "Generate grid" again once the grid is empty.`,
ui.ButtonSet.OK
githubProject = await fetchProjectFromGitHub(
repo.owner === 'w3c' ? repo.owner : `user/${repo.owner}`,
repo.name,
template
);
}
else {
reportError(`Unexpected HTTP status ${status} received from GitHub.
catch (err) {
reportError(err.toString());
return;
}

Data could not be imported from ${repo}.`);
try {
refreshProject(SpreadsheetApp.getActiveSpreadsheet(), githubProject, {
what: 'all'
});
}
catch(err) {
reportError(err.toString());
return;
}

const htmlOutput = HtmlService
.createHtmlOutput(
'<pre>' + JSON.stringify(githubProject, null, 2) + '</pre>'
)
.setWidth(300)
.setHeight(400);
SpreadsheetApp.getUi().showModalDialog(htmlOutput, 'GitHub project');
}
4 changes: 4 additions & 0 deletions tools/appscript/main.mjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import _createOnOpenTrigger from './create-onopen-trigger.mjs';
import _addTPACMenu from './add-custom-menu.mjs';
import _importFromGitHub from './import-from-github.mjs';
import _exportToGitHub from './export-to-github.mjs';
import _generateGrid from './generate-grid.mjs';
import _validateGrid from './validate-grid.mjs';
import _exportEventData from './export-event-data.mjs';

function main() { _createOnOpenTrigger(); }
function addTPACMenu() { _addTPACMenu(); }
function importFromGitHub() { _importFromGitHub(); }
function exportToGitHub() { _exportToGitHub(); }
function generateGrid() { _generateGrid(); }
function validateGrid() { _validateGrid(); }
function exportEventData() { _exportEventData(); }
Loading

0 comments on commit 0151b1d

Please sign in to comment.