Mathematica Extension demo refresh

This commit is contained in:
SheetJS 2024-06-05 23:56:24 -04:00
parent 5f443931f8
commit 67294eeeae

@ -25,7 +25,12 @@ data from opaque spreadsheets and parse the data from Mathematica.
:::note Tested Deployments
This demo was last tested by SheetJS users on 2023 November 04 in Mathematica 13.
This demo was tested by SheetJS users in the following deployments:
| Architecture | Version | Date |
|:-------------|:--------|:-----------|
| `darwin-x64` | `14.0` | 2024-06-05 |
| `win10-x64` | `14.0` | 2024-06-05 |
:::
@ -81,7 +86,7 @@ Dataset object assuming one header row.
<Tabs groupId="os">
<TabItem value="unix" label="Linux/MacOS">
```mathematica title="Complete Function"
```mathematica title="SheetJSImportFileEE"
(* Import file stored in the Documents folder (e.g. C:\Users\Me\Documents) *)
SheetJSImportFileEE[filename_]:=Module[{csv}, (
(* This was required in local testing *)
@ -107,7 +112,7 @@ SheetJSImportFileEE[filename_]:=Module[{csv}, (
</TabItem>
<TabItem value="win" label="Windows">
```mathematica title="Complete Function"
```mathematica title="SheetJSImportFileEE"
(* Import file stored in the Documents folder (e.g. C:\Users\Me\Documents) *)
SheetJSImportFileEE[filename_]:=Module[{csv}, (
(* This was required in local testing *)
@ -133,70 +138,6 @@ SheetJSImportFileEE[filename_]:=Module[{csv}, (
</TabItem>
</Tabs>
<details open>
<summary><b>How to run the example</b> (click to hide)</summary>
:::note Tested Deployments
This example was last tested on 2023 November 04 with Mathematica 13.3.
:::
0) Install NodeJS. When the demo was tested, version `20.9.0` was installed.
1) Install dependencies in the Home folder (`~` or `$HOME` or `%HOMEPATH%`):
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz zeromq@6.0.0-beta.17`}
</CodeBlock>
2) Open a new Mathematica Notebook and register NodeJS. When the example was
tested in Windows, the commands were:
<Tabs groupId="os">
<TabItem value="unix" label="Linux/MacOS">
```mathematica
RegisterExternalEvaluator["NodeJS","/usr/local/bin/node"]
FindExternalEvaluators["NodeJS"]
```
</TabItem>
<TabItem value="win" label="Windows">
```mathematica
RegisterExternalEvaluator["NodeJS","C:\\Program Files\\nodejs\\node.exe"]
FindExternalEvaluators["NodeJS"]
```
</TabItem>
</Tabs>
The second argument to `RegisterExternalEvaluator` should be the path to the
`node` or `node.exe` binary.
If NodeJS is registered, the value in the "Registered" column will be "True".
4) To determine the base folder, run `require("process").cwd()` from NodeJS:
```mathematica
ExternalEvaluate["NodeJS", "require('process').cwd()"]
```
5) Download [`pres.numbers`](https://docs.sheetjs.com/pres.numbers) and move
the file to the base folder as shown in the previous step.
6) Copy and evaluate the "Complete Function" in the previous codeblock.
7) Run the function and confirm the result is a proper Dataset:
```mathematica
SheetJSImportFileEE["pres.numbers"]
```
![SheetJSImportFileEE result](pathname:///mathematica/SheetJSImportFileEE.png)
</details>
### Command-Line Tools
@ -236,31 +177,177 @@ flowchart LR
## Complete Demo
1) Create the standalone `xlsx-cli` binary[^14]:
This demo tests the NodeJS external engine and dedicated command line tools.
### NodeJS Engine
0) Install NodeJS. When the demo was tested, version `20.14.0` was installed.
1) Install dependencies in the Home folder (`~` or `$HOME` or `%HOMEPATH%`):
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz zeromq@6.0.0-beta.19`}
</CodeBlock>
2) Open a new Mathematica Notebook and register NodeJS. When the example was
tested in Windows, the commands were:
<Tabs groupId="os">
<TabItem value="unix" label="Linux/MacOS">
```mathematica
RegisterExternalEvaluator["NodeJS","/usr/local/bin/node"]
FindExternalEvaluators["NodeJS"]
```
The second argument to `RegisterExternalEvaluator` should be the path to the
`node` program, which can be found by running the following command in a new
terminal window:
```bash
which node
```
</TabItem>
<TabItem value="win" label="Windows">
```mathematica
RegisterExternalEvaluator["NodeJS","C:\\Program Files\\nodejs\\node.exe"]
FindExternalEvaluators["NodeJS"]
```
The second argument to `RegisterExternalEvaluator` should be the path to the
`node.exe` program, which can be found by running the following command in a new
PowerShell window:
```powershell
Get-Command node.exe
```
</TabItem>
</Tabs>
If NodeJS is registered, the value in the "Registered" column will be "True".
4) To determine the base folder, run `require("process").cwd()` from NodeJS:
```mathematica
ExternalEvaluate["NodeJS", "require('process').cwd()"]
```
5) Download [`pres.numbers`](https://docs.sheetjs.com/pres.numbers) and move
the file to the base folder as shown in the previous step.
3) Copy, but do not run, the following snippet into the running notebook:
<Tabs groupId="os">
<TabItem value="unix" label="Linux/MacOS">
```mathematica title="SheetJSImportFileEE"
(* Import file stored in the Documents folder (e.g. C:\Users\Me\Documents) *)
SheetJSImportFileEE[filename_]:=Module[{csv}, (
(* This was required in local testing *)
/* highlight-next-line */
RegisterExternalEvaluator["NodeJS","/usr/local/bin/node"];
(* Generate CSV from first sheet *)
csv:=ExternalEvaluate["NodeJS", StringJoin[
(* module installed in home directory *)
"var XLSX = require('xlsx');",
(* read specified filename *)
"var wb = XLSX.readFile('",filename,"');",
(* grab first worksheet *)
"var ws = wb.Sheets[wb.SheetNames[0]];",
(* convert to CSV *)
"XLSX.utils.sheet_to_csv(ws)"
]];
(* Parse CSV into a dataset *)
Return[ImportString[csv, "Dataset", "HeaderLines"->1]];
)]
```
</TabItem>
<TabItem value="win" label="Windows">
```mathematica title="SheetJSImportFileEE"
(* Import file stored in the Documents folder (e.g. C:\Users\Me\Documents) *)
SheetJSImportFileEE[filename_]:=Module[{csv}, (
(* This was required in local testing *)
/* highlight-next-line */
RegisterExternalEvaluator["NodeJS","C:\\Program Files\\nodejs\\node.exe"];
(* Generate CSV from first sheet *)
csv:=ExternalEvaluate["NodeJS", StringJoin[
(* module installed in home directory *)
"var XLSX = require('xlsx');",
(* read specified filename *)
"var wb = XLSX.readFile('",filename,"');",
(* grab first worksheet *)
"var ws = wb.Sheets[wb.SheetNames[0]];",
(* convert to CSV *)
"XLSX.utils.sheet_to_csv(ws)"
]];
(* Parse CSV into a dataset *)
Return[ImportString[csv, "Dataset", "HeaderLines"->1]];
)]
```
</TabItem>
</Tabs>
After pasting, edit the highlighted line to reflect the path of the `node` or
`node.exe` binary. This path was discovered in Step 2.
After editing the snippet, run the expression.
7) Run the function and confirm the result is a proper Dataset:
```mathematica
SheetJSImportFileEE["pres.numbers"]
```
![SheetJSImportFileEE result](pathname:///mathematica/SheetJSImportFileEE.png)
### Standalone Binary
8) Create the standalone `xlsx-cli` binary[^14]. The commands should be run in a
Terminal or PowerShell window:
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz exit-on-epipe commander@2
curl -LO https://docs.sheetjs.com/cli/xlsx-cli.js
npx nexe -t 14.15.3 xlsx-cli.js`}
npx -y nexe -t 14.15.3 xlsx-cli.js`}
</CodeBlock>
<Tabs groupId="os">
<TabItem value="unix" label="Linux/MacOS">
2) Move the generated `xlsx-cli` to a fixed location in `/usr/local/bin`:
9) Move the generated `xlsx-cli` to a fixed location in `/usr/local/bin`:
```bash
mkdir -p /usr/local/bin
mv xlsx-cli /usr/local/bin/
```
:::note pass
If there are permission errors, the command should be run with the root user:
```bash
sudo mv xlsx-cli /usr/local/bin/
```
:::
</TabItem>
<TabItem value="win" label="Windows">
2) Find the current directory:
9) Find the current directory:
```bash
cd
pwd
```
The generated binary will be `xlsx-cli.exe` in the displayed path.
@ -270,9 +357,12 @@ The generated binary will be `xlsx-cli.exe` in the displayed path.
### Reading a Local File
3) In a new Mathematica notebook, run the following snippet:
<Tabs groupId="os">
<TabItem value="unix" label="Linux/MacOS">
```mathematica
10) In a new Mathematica notebook, run the following snippet:
```mathematica title="SheetJSImportFile"
SheetJSImportFile[x_] := ImportString[Block[{Print}, ExternalEvaluate[
"Shell" -> "StandardOutput",
// highlight-next-line
@ -280,17 +370,24 @@ SheetJSImportFile[x_] := ImportString[Block[{Print}, ExternalEvaluate[
]], "Dataset", "HeaderLines" -> 1]
```
<Tabs groupId="os">
<TabItem value="unix" label="Linux/MacOS">
</TabItem>
<TabItem value="win" label="Windows">
10) In a new Mathematica notebook, copy but do not run the following snippet:
```mathematica title="SheetJSImportFile"
SheetJSImportFile[x_] := ImportString[Block[{Print}, ExternalEvaluate[
"Shell" -> "StandardOutput",
// highlight-next-line
"/usr/local/bin/xlsx-cli " <> x
]], "Dataset", "HeaderLines" -> 1]
```
Change `/usr/local/bin/xlsx-cli` in the string to the path to the generated
`xlsx-cli.exe` binary. For example, if the path in step 2 was
`C:\Users\Me\Documents\`, then the code should be:
```mathematica
```mathematica title="SheetJSImportFile"
SheetJSImportFile[x_] := ImportString[Block[{Print}, ExternalEvaluate[
"Shell" -> "StandardOutput",
// highlight-next-line
@ -298,25 +395,58 @@ SheetJSImportFile[x_] := ImportString[Block[{Print}, ExternalEvaluate[
]], "Dataset", "HeaderLines" -> 1]
```
The `\` characters must be doubled.
:::info pass
Mathematica requires the `\` characters must be doubled.
:::
After making the change, run the snippet.
</TabItem>
</Tabs>
4) Download https://docs.sheetjs.com/pres.numbers and save to Downloads folder:
11) Download https://docs.sheetjs.com/pres.numbers and save to Downloads folder:
```bash
cd ~/Downloads/
curl -LO https://docs.sheetjs.com/pres.numbers
```
5) In the Mathematica notebook, run the new function. If the file was saved to
12) In the Mathematica notebook, run the new function. If the file was saved to
the Downloads folder, the path will be `"~/Downloads/pres.numbers"` in macOS:
<Tabs groupId="os">
<TabItem value="unix" label="Linux/MacOS">
```mathematica
data = SheetJSImportFile["~/Downloads/pres.numbers"]
```
</TabItem>
<TabItem value="win" label="Windows">
On Windows, the absolute path to the file must be used. To find this path, run
the following commands in PowerShell:
```powershell
cd $HOME\Downloads
pwd
```
Append `\\pres.numbers` to the displayed path. For example, if the path was
`C:\Users\Me\Downloads`, the command will be
```mathematica
data = SheetJSImportFile["C:\\Users\\Me\\Downloads\\pres.numbers"]
```
The `\` characters must be doubled.
</TabItem>
</Tabs>
The result should be displayed in a concise table.
![SheetJSImportFile result](pathname:///mathematica/SheetJSImportFile.png)
@ -326,9 +456,9 @@ The result should be displayed in a concise table.
`FetchURL`[^15] downloads a file from a specified URL and returns a path to the
file. This function will be wrapped in a new function called `SheetJSImportURL`.
6) In the same notebook, run the following:
13) In the same notebook, run the following:
```mathematica
```mathematica title="SheetJSImportURL"
Needs["Utilities`URLTools`"];
SheetJSImportURL[x_] := Module[{path},(
path = FetchURL[x];
@ -336,7 +466,7 @@ SheetJSImportURL[x_] := Module[{path},(
)];
```
7) Test by downloading the test file in the notebook:
14) Test by downloading the test file in the notebook:
```mathematica
data = SheetJSImportURL["https://docs.sheetjs.com/pres.numbers"]