Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/jupyter/ipynb/parse.ts
1447 views
1
/*
2
This is a very lightweight ipynb parser in pure javascript.
3
4
It's used, e.g., in our analogue of nbviewer.
5
*/
6
7
import { IPynbImporter } from "@cocalc/jupyter/ipynb/import-from-ipynb";
8
import { JUPYTER_MIMETYPES } from "@cocalc/jupyter/util/misc";
9
import { field_cmp } from "@cocalc/util/misc";
10
11
export interface KernelSpec {
12
language?: string;
13
display_name: string;
14
name: string;
15
}
16
17
export interface CoCalcJupyter {
18
cellList: string[];
19
cells: { [id: string]: object };
20
cmOptions?: { [field: string]: any };
21
metadata?;
22
kernelspec: KernelSpec;
23
}
24
25
export default function parse(content: string): CoCalcJupyter {
26
const ipynb = JSON.parse(content);
27
const importer = new IPynbImporter();
28
importer.import({
29
ipynb,
30
output_handler: (cell) => {
31
let k: number = 0;
32
return {
33
message: (content) => {
34
process(content);
35
cell.output[`${k}`] = content;
36
k += 1;
37
},
38
};
39
},
40
});
41
42
const cells = importer.cells();
43
const cellList = sortedCellList(cells);
44
const cmOptions = getCMOptions(getMode(ipynb));
45
const kernelspec = ipynb.metadata?.kernelspec ?? {
46
display_name: "Unknown Kernel",
47
};
48
return { cells, cellList, cmOptions, kernelspec };
49
}
50
51
export function getMode(ipynb): string {
52
return (
53
ipynb.metadata?.language_info?.codemirror_mode ??
54
ipynb.metadata?.language_info?.name ??
55
ipynb.metadata?.kernelspec?.language?.toLowerCase() ??
56
"python"
57
);
58
}
59
60
export function getCMOptions(
61
mode: string | { name: string } | undefined | null,
62
) {
63
if (mode == null) {
64
mode = { name: "python" };
65
}
66
if (typeof mode === "string") {
67
mode = { name: mode };
68
}
69
if (mode.name.includes("python") || mode.name.includes("sage")) {
70
mode.name = "python";
71
} else if (mode.name === "gp") {
72
mode.name = "pari";
73
} else if (mode.name === "singular") {
74
mode.name = "clike"; // better than nothing
75
} else if (mode.name === "ihaskell") {
76
mode.name = "haskell";
77
}
78
79
return {
80
mode,
81
showTrailingSpace: true,
82
tabSize: 4,
83
lineWrapping: true,
84
readOnly: true,
85
};
86
}
87
88
function process(content): void {
89
if (content?.data == null) {
90
return;
91
}
92
for (const type of JUPYTER_MIMETYPES) {
93
if (
94
content.data[type] != null &&
95
(type.split("/")[0] === "image" || type === "application/pdf")
96
) {
97
content.data[type] = { value: content.data[type] };
98
}
99
}
100
}
101
102
function sortedCellList(cells): string[] {
103
// Given map from id's to cells, returns an list of ids in correct order,
104
// as defined by pos field.
105
const v: { id: string; pos: number }[] = [];
106
for (const id in cells) {
107
v.push({ id, pos: cells[id]?.pos ?? -1 });
108
}
109
v.sort(field_cmp("pos"));
110
const a: string[] = [];
111
for (const { id } of v) {
112
a.push(id);
113
}
114
return a;
115
}
116
117