Google Sheets demo refresh
This commit is contained in:
parent
a5e01f9476
commit
55ce74bf8c
@ -81,6 +81,12 @@ do not work in MDX v2. Instead, string literals and concatenation must be used:
|
||||
<a href={"Foo" + current + ""}>{"Foo" + current + ""}</a>
|
||||
```
|
||||
|
||||
**Tables**
|
||||
|
||||
MDX inconsistently requires different indentation levels for `TD` / `TH`, `TR`,
|
||||
`THEAD` / `TBODY` / `TFOOT`, and `TABLE` tags. Unconventional indentation is
|
||||
intentional.
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
|
@ -72,7 +72,11 @@ The script https://docs.sheetjs.com/dojo/dojo.js was fetched from the official
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
The demos were last tested on 2023-12-04.
|
||||
This demo was tested in the following environments:
|
||||
|
||||
| Platform | Date |
|
||||
|:-------------|:-----------|
|
||||
| Chromium 125 | 2024-06-08 |
|
||||
|
||||
Demos exclusively using Dojo Core were tested using Dojo Toolkit `1.17.3`.
|
||||
|
||||
|
@ -34,8 +34,8 @@ This demo was tested in the following environments:
|
||||
|
||||
| Version | Date |
|
||||
|:---------|:-----------|
|
||||
| `2.10.3` | 2023-12-04 |
|
||||
| `1.12.3` | 2023-12-04 |
|
||||
| `2.12.0` | 2024-06-08 |
|
||||
| `1.12.4` | 2024-06-08 |
|
||||
|
||||
:::
|
||||
|
||||
@ -135,7 +135,7 @@ main `index.html` page should load the `index.js` script:
|
||||
<body>
|
||||
<h3>SheetJS <span id="vers"></span> export demo</h3>
|
||||
<button id="xport">Click to Export!</button>
|
||||
<script src="index.js"></script>
|
||||
<script src="index.js" type="module"></script>
|
||||
<body>
|
||||
```
|
||||
|
||||
@ -208,7 +208,7 @@ yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||||
3) Run the ParcelJS development server:
|
||||
|
||||
```bash
|
||||
npx -y parcel@2.10.3 index.html
|
||||
npx -y parcel index.html
|
||||
```
|
||||
|
||||
The process will print a URL:
|
||||
@ -231,7 +231,7 @@ a web browser and click the "Click to Export!" button to generate a file.
|
||||
6) Build the production site:
|
||||
|
||||
```bash
|
||||
npx -y parcel@2.10.0 build index.html
|
||||
npx -y parcel build index.html
|
||||
```
|
||||
|
||||
The production site will be stored in the `dist` folder
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -32,7 +32,12 @@ flowchart LR
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was last tested by SheetJS users on 2024-04-25 in Maple 2024.
|
||||
This demo was tested by SheetJS users in the following deployments:
|
||||
|
||||
| Architecture | Version | Date |
|
||||
|:-------------|:--------|:-----------|
|
||||
| `darwin-x64` | 2024 | 2024-04-25 |
|
||||
| `win10-x64` | 2024 | 2024-04-25 |
|
||||
|
||||
:::
|
||||
|
||||
|
BIN
docz/static/gsheet/SheetJS1.png
Normal file
BIN
docz/static/gsheet/SheetJS1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
BIN
docz/static/gsheet/SheetJS2.png
Normal file
BIN
docz/static/gsheet/SheetJS2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
BIN
docz/static/gsheet/creds.png
Normal file
BIN
docz/static/gsheet/creds.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 55 KiB |
39
docz/static/gsheet/dump.mjs
Normal file
39
docz/static/gsheet/dump.mjs
Normal file
@ -0,0 +1,39 @@
|
||||
import { google } from "googleapis";
|
||||
|
||||
import { set_fs, writeFile, utils } from 'xlsx';
|
||||
import * as fs from 'fs';
|
||||
set_fs(fs);
|
||||
|
||||
/* Change this import statement to point to the credentials JSON file */
|
||||
import creds from './sheetjs-test-726272627262.json' assert { type: "json" };
|
||||
|
||||
/* Change this to the spreadsheet ID */
|
||||
const id = "SOME-SPREADSHEETJS-ID";
|
||||
|
||||
/* connect to google services */
|
||||
const jwt = new google.auth.JWT({
|
||||
email: creds.client_email,
|
||||
key: creds.private_key,
|
||||
scopes: [
|
||||
'https://www.googleapis.com/auth/spreadsheets',
|
||||
'https://www.googleapis.com/auth/drive.file',
|
||||
]
|
||||
});
|
||||
|
||||
const sheets = google.sheets({ version: "v4", auth: jwt });
|
||||
|
||||
/* get existing sheets */
|
||||
const wsheet = await sheets.spreadsheets.get({spreadsheetId: id});
|
||||
|
||||
/* create a workbook */
|
||||
const wb = utils.book_new();
|
||||
|
||||
for(let sheet of wsheet.data.sheets) {
|
||||
const name = sheet.properties.title;
|
||||
const res = await sheets.spreadsheets.values.get({
|
||||
spreadsheetId: id,
|
||||
range: `'${name}'`
|
||||
});
|
||||
utils.book_append_sheet(wb, utils.aoa_to_sheet(res.data.values), name);
|
||||
}
|
||||
writeFile(wb, "SheetJSExport.xlsb");
|
87
docz/static/gsheet/init.mjs
Normal file
87
docz/static/gsheet/init.mjs
Normal file
@ -0,0 +1,87 @@
|
||||
import { google } from "googleapis";
|
||||
|
||||
/* Change this import statement to point to the credentials JSON file */
|
||||
import creds from './sheetjs-test-726272627262.json' assert { type: "json" };
|
||||
|
||||
/* Change this to the primary account address, NOT THE SERVICE ACCOUNT */
|
||||
const acct = "YOUR_ADDRESS@gmail.com";
|
||||
|
||||
/* connect to google services */
|
||||
const jwt = new google.auth.JWT({
|
||||
email: creds.client_email,
|
||||
key: creds.private_key,
|
||||
scopes: [
|
||||
'https://www.googleapis.com/auth/spreadsheets',
|
||||
'https://www.googleapis.com/auth/drive.file',
|
||||
]
|
||||
});
|
||||
|
||||
const sheets = google.sheets({ version: "v4", auth: jwt });
|
||||
const drive = google.drive({version: "v3", auth: jwt });
|
||||
|
||||
/* create new google workbook */
|
||||
const [id, sheet0id] = await (async() => {
|
||||
const res = await sheets.spreadsheets.create({
|
||||
requestBody: {
|
||||
properties: {
|
||||
title: "SheetJS Test"
|
||||
}
|
||||
}
|
||||
});
|
||||
const id = res.data.spreadsheetId;
|
||||
const sheet0id = res.data.sheets[0].properties.sheetId;
|
||||
return [id, sheet0id];
|
||||
})();
|
||||
console.log(`Created Google Workbook ${id}`);
|
||||
|
||||
/* create new google worksheet and delete initial sheet */
|
||||
const [sheet1id, sheet2id] = await (async() => {
|
||||
const res = await sheets.spreadsheets.batchUpdate({
|
||||
spreadsheetId: id,
|
||||
requestBody: { requests: [
|
||||
/* add SheetJS1 */
|
||||
{ addSheet: { properties: { title: "SheetJS1" } } },
|
||||
/* add SheetJS2 */
|
||||
{ addSheet: { properties: { title: "SheetJS2" } } },
|
||||
/* remove default sheet */
|
||||
{ deleteSheet: { sheetId: sheet0id } },
|
||||
] }
|
||||
});
|
||||
console.log(`Created Google Worksheets "SheetJS1" and "SheetJS2"`);
|
||||
return res.data.replies.slice(0,2).map(r => r.addSheet.properties.sheetId);
|
||||
})();
|
||||
|
||||
await sheets.spreadsheets.values.update({
|
||||
spreadsheetId: id,
|
||||
range: "SheetJS1!A1",
|
||||
valueInputOption: "USER_ENTERED",
|
||||
resource: { values: [
|
||||
["Sheet", "JS"],
|
||||
[72, 62]
|
||||
]}
|
||||
});
|
||||
|
||||
await sheets.spreadsheets.values.update({
|
||||
spreadsheetId: id,
|
||||
range: "SheetJS2!A1",
|
||||
valueInputOption: "USER_ENTERED",
|
||||
resource: { values: [
|
||||
["Area Code", "Part 1", "Part 2"],
|
||||
[201, 867, 5309],
|
||||
[281, 330, 8004],
|
||||
]}
|
||||
});
|
||||
|
||||
/* Share new Document with the primary account */
|
||||
try {
|
||||
await drive.permissions.create({
|
||||
fileId: id,
|
||||
fields: "id",
|
||||
requestBody: {
|
||||
type: "user",
|
||||
role: "writer",
|
||||
emailAddress: acct
|
||||
}
|
||||
});
|
||||
console.log(`Shared ${id} with ${acct}`);
|
||||
} catch(e) { console.log(e); }
|
87
docz/static/gsheet/load.mjs
Normal file
87
docz/static/gsheet/load.mjs
Normal file
@ -0,0 +1,87 @@
|
||||
import { google } from "googleapis";
|
||||
|
||||
import { set_fs, readFile, utils } from 'xlsx';
|
||||
import * as fs from 'fs';
|
||||
set_fs(fs);
|
||||
|
||||
/* Change this import statement to point to the credentials JSON file */
|
||||
import creds from './sheetjs-test-726272627262.json' assert { type: "json" };
|
||||
|
||||
/* Change this to the spreadsheet ID */
|
||||
const id = "SOME-SPREADSHEETJS-ID";
|
||||
|
||||
/* connect to google services */
|
||||
const jwt = new google.auth.JWT({
|
||||
email: creds.client_email,
|
||||
key: creds.private_key,
|
||||
scopes: [
|
||||
'https://www.googleapis.com/auth/spreadsheets',
|
||||
'https://www.googleapis.com/auth/drive.file',
|
||||
]
|
||||
});
|
||||
|
||||
const sheets = google.sheets({ version: "v4", auth: jwt });
|
||||
|
||||
/* get existing sheets */
|
||||
const wsheet = await sheets.spreadsheets.get({spreadsheetId: id});
|
||||
|
||||
/* remove all sheets after the first */
|
||||
if(wsheet.data.sheets.length > 1) await sheets.spreadsheets.batchUpdate({
|
||||
spreadsheetId: id,
|
||||
requestBody: { requests:
|
||||
wsheet.data.sheets.slice(1).map(s => ({
|
||||
deleteSheet: {
|
||||
sheetId: s.properties.sheetId
|
||||
}
|
||||
}))
|
||||
}
|
||||
});
|
||||
|
||||
/* read file */
|
||||
const wb = readFile("pres.numbers");
|
||||
|
||||
/* rename first worksheet to avoid collisions */
|
||||
const props0 = wsheet.data.sheets[0].properties;
|
||||
if(wb.SheetNames.map(n => n.toLowerCase()).includes(props0.title.toLowerCase())) {
|
||||
await sheets.spreadsheets.batchUpdate({
|
||||
spreadsheetId: id,
|
||||
requestBody: { requests: [{
|
||||
updateSheetProperties: {
|
||||
fields: "title",
|
||||
properties: {
|
||||
sheetId: props0.sheetId,
|
||||
title: "thistitleisatleast33characterslong"
|
||||
}
|
||||
}
|
||||
}]}
|
||||
});
|
||||
console.log(`renamed "${props0.title}" to "thistitleisatleast33characterslong"`);
|
||||
}
|
||||
|
||||
/* add sheets from file */
|
||||
for(let name of wb.SheetNames) {
|
||||
const aoa = utils.sheet_to_json(wb.Sheets[name], {header:1});
|
||||
await sheets.spreadsheets.batchUpdate({
|
||||
spreadsheetId: id,
|
||||
requestBody: { requests: [
|
||||
/* add new sheet */
|
||||
{ addSheet: { properties: { title: name } } },
|
||||
] }
|
||||
});
|
||||
await sheets.spreadsheets.values.update({
|
||||
spreadsheetId: id,
|
||||
range: `'${name}'!A1`,
|
||||
valueInputOption: "USER_ENTERED",
|
||||
resource: { values: aoa }
|
||||
});
|
||||
console.log(`Created Google Worksheet "${name}"`);
|
||||
}
|
||||
|
||||
/* remove first sheet */
|
||||
const res = await sheets.spreadsheets.batchUpdate({
|
||||
spreadsheetId: id,
|
||||
requestBody: { requests: [
|
||||
/* remove old first sheet */
|
||||
{ deleteSheet: { sheetId: wsheet.data.sheets[0].properties.sheetId } }
|
||||
] }
|
||||
});
|
BIN
docz/static/gsheet/notification.png
Normal file
BIN
docz/static/gsheet/notification.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 37 KiB |
38
docz/static/gsheet/raw.mjs
Normal file
38
docz/static/gsheet/raw.mjs
Normal file
@ -0,0 +1,38 @@
|
||||
import { google } from "googleapis";
|
||||
|
||||
import { read, utils } from 'xlsx';
|
||||
|
||||
/* Change this import statement to point to the credentials JSON file */
|
||||
import creds from './sheetjs-test-726272627262.json' assert { type: "json" };
|
||||
|
||||
/* Change this to the spreadsheet ID */
|
||||
const id = "SOME-SPREADSHEETJS-ID";
|
||||
|
||||
/* connect to google services */
|
||||
const jwt = new google.auth.JWT({
|
||||
email: creds.client_email,
|
||||
key: creds.private_key,
|
||||
scopes: [
|
||||
'https://www.googleapis.com/auth/spreadsheets',
|
||||
'https://www.googleapis.com/auth/drive.file',
|
||||
]
|
||||
});
|
||||
|
||||
const drive = google.drive({ version: "v3", auth: jwt });
|
||||
|
||||
/* get XLSX export */
|
||||
const file = await drive.files.export({
|
||||
mimeType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
fileId: id
|
||||
});
|
||||
const ab = await file.data.arrayBuffer();
|
||||
|
||||
/* parse with SheetJS */
|
||||
const wb = read(ab);
|
||||
|
||||
/* print CSV data from each worksheet */
|
||||
wb.SheetNames.forEach(n => {
|
||||
console.log(`#### ${n}`);
|
||||
console.log(utils.sheet_to_csv(wb.Sheets[n]));
|
||||
console.log("");
|
||||
})
|
BIN
docz/static/gsheet/selector.png
Normal file
BIN
docz/static/gsheet/selector.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 50 KiB |
93
tests/bundler-parcel.sh
Executable file
93
tests/bundler-parcel.sh
Executable file
@ -0,0 +1,93 @@
|
||||
#!/bin/bash
|
||||
# https://docs.sheetjs.com/docs/demos/frontend/bundler/parcel
|
||||
cd /tmp
|
||||
rm -rf sheetjs-parceljs
|
||||
|
||||
mkdir sheetjs-parceljs
|
||||
cd sheetjs-parceljs
|
||||
npm init -y
|
||||
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz
|
||||
|
||||
cat >index.html <<EOF
|
||||
<body>
|
||||
<h3>SheetJS <span id="vers"></span> export demo</h3>
|
||||
<button id="xport">Click to Export!</button>
|
||||
<script src="index.js" type="module"></script>
|
||||
<body>
|
||||
EOF
|
||||
|
||||
cat >index.js <<EOF
|
||||
// ESM-style import from "xlsx"
|
||||
import { utils, version, writeFileXLSX } from 'xlsx';
|
||||
|
||||
document.getElementById("vers").innerText = version;
|
||||
document.getElementById("xport").onclick = async() => {
|
||||
/* fetch JSON data and parse */
|
||||
const url = "https://docs.sheetjs.com/executive.json";
|
||||
const raw_data = await (await fetch(url)).json();
|
||||
|
||||
/* filter for the Presidents */
|
||||
const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));
|
||||
|
||||
/* sort by first presidential term */
|
||||
prez.forEach(row => row.start = row.terms.find(term => term.type === "prez").start);
|
||||
prez.sort((l,r) => l.start.localeCompare(r.start));
|
||||
|
||||
/* flatten objects */
|
||||
const rows = prez.map(row => ({
|
||||
name: row.name.first + " " + row.name.last,
|
||||
birthday: row.bio.birthday
|
||||
}));
|
||||
|
||||
/* generate worksheet and workbook */
|
||||
const worksheet = utils.json_to_sheet(rows);
|
||||
const workbook = utils.book_new();
|
||||
utils.book_append_sheet(workbook, worksheet, "Dates");
|
||||
|
||||
/* fix headers */
|
||||
utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
|
||||
|
||||
/* calculate column width */
|
||||
const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
|
||||
worksheet["!cols"] = [ { wch: max_width } ];
|
||||
|
||||
/* create an XLSX file and try to save to Presidents.xlsx */
|
||||
writeFileXLSX(workbook, "Presidents.xlsx");
|
||||
};
|
||||
EOF
|
||||
|
||||
cat >test.js <<EOF
|
||||
const puppeteer = require('puppeteer');
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
app.use(express.static('./dist'));
|
||||
app.listen(7262, async() => {
|
||||
await new Promise((res,rej) => setTimeout(res, 1000));
|
||||
const browser = await puppeteer.launch();
|
||||
const page = await browser.newPage();
|
||||
page.on("console", msg => console.log("PAGE LOG:", msg.text()));
|
||||
await page.setViewport({width: 1920, height: 1080});
|
||||
const client = await page.target().createCDPSession();
|
||||
await client.send('Browser.setDownloadBehavior', {
|
||||
behavior: 'allow',
|
||||
downloadPath: require("path").resolve('./')
|
||||
});
|
||||
page.on('request', req => console.log(req.url()));
|
||||
await page.goto('http://localhost:7262/');
|
||||
await new Promise((res,rej) => setTimeout(res, 1000));
|
||||
await page.click("#xport");
|
||||
await new Promise((res,rej) => setTimeout(res, 1000));
|
||||
await browser.close();
|
||||
process.exit();
|
||||
});
|
||||
EOF
|
||||
|
||||
node -e 'var pjson = JSON.parse(fs.readFileSync("./package.json")); console.log(pjson); delete pjson.main; fs.writeFileSync("package.json", JSON.stringify(pjson))'
|
||||
|
||||
for n in 1.12.4 2.12.0; do
|
||||
npx -y parcel build index.html
|
||||
node test.js
|
||||
npx -y xlsx-cli Presidents.xlsx | head -n 3
|
||||
rm -f Presidents.xlsx
|
||||
done
|
Loading…
Reference in New Issue
Block a user