CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/client/welcome-file.ts
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
/**
7
* Anonymous User Welcome File
8
*
9
* The goal is to present an anonymous user with a file/editor matching a specific intention.
10
* What exactly is open for experientation, but it is clear that if you want to run "latex",
11
* you're not interested in working with a "juypter notebook".
12
*/
13
14
import { delay } from "awaiting";
15
16
import { once } from "@cocalc/util/async-utils";
17
import { redux } from "@cocalc/frontend/app-framework";
18
import { QueryParams } from "../misc/query-params";
19
import { separate_file_extension } from "@cocalc/util/misc";
20
import type { JupyterActions } from "@cocalc/jupyter/redux/actions";
21
22
type Kernel = "ir" | "python3" | "bash" | "octave";
23
type Cell = { type?: "markdown" | "code"; content: string };
24
25
const ir_welcome = `# Welcome to R in CoCalc!
26
27
Run a cell via \`Shift + Return\`. Learn more about [CoCalc Jupyter Notebooks](https://doc.cocalc.com/jupyter.html).`;
28
29
const python3_welcome = `# Welcome to Python in CoCalc!
30
31
Run a cell via \`Shift + Return\`. Learn more about [CoCalc Jupyter Notebooks](https://doc.cocalc.com/jupyter.html).`;
32
33
const bash_welcome = `# Welcome to Bash in CoCalc!
34
35
Run a cell via \`Shift + Return\`. Learn more about [CoCalc Jupyter Notebooks](https://doc.cocalc.com/jupyter.html).`;
36
37
const octave_welcome = `# Welcome to Octave in CoCalc!
38
39
Run a cell via \`Shift + Return\`. Learn more about [CoCalc Jupyter Notebooks](https://doc.cocalc.com/jupyter.html).`;
40
41
const WelcomeSetups: Record<Kernel, Cell[]> = {
42
ir: [
43
{ type: "markdown", content: ir_welcome },
44
{ content: "data <- rnorm(100)\nsummary(data)" },
45
{ content: "hist(data)" },
46
],
47
python3: [
48
{ type: "markdown", content: python3_welcome },
49
{ content: "import sys\nsys.version" },
50
{ content: "import matplotlib.pyplot as plt\nimport numpy as np" },
51
{
52
content: `xx = np.linspace(0, 10 * np.pi, 1000)
53
yy = np.sin(xx) * np.exp(-xx / 10)
54
plt.grid()
55
plt.plot(xx, yy)`,
56
},
57
],
58
bash: [
59
{ type: "markdown", content: bash_welcome },
60
{ content: 'foo="World"\necho "Hello $foo!"' },
61
{ content: "date" },
62
{ content: "echo '2*20 + 2' | bc -l" },
63
],
64
octave: [
65
{ type: "markdown", content: octave_welcome },
66
{
67
content: `x = 1:10;
68
y = 1:10;
69
x' * y`,
70
},
71
{
72
content: `tx = ty = linspace (-8, 8, 41)';
73
[xx, yy] = meshgrid (tx, ty);
74
r = sqrt (xx .^ 2 + yy .^ 2) + eps;
75
tz = sin (r) ./ r;
76
mesh (tx, ty, tz);
77
xlabel ("tx");
78
ylabel ("ty");
79
zlabel ("tz");
80
title ("3-D Sombrero plot");`,
81
},
82
],
83
};
84
85
export class WelcomeFile {
86
private readonly project_id: string;
87
private readonly param: string;
88
private readonly path: string | undefined;
89
90
constructor(project_id: string) {
91
this.project_id = project_id;
92
const qparam = QueryParams.get("anonymous");
93
if (qparam != null) {
94
this.param = (Array.isArray(qparam) ? qparam[0] : qparam).toLowerCase();
95
}
96
if (this.param == null) return;
97
this.path = this.make_path();
98
}
99
100
async open() {
101
if (this.path == null) return;
102
await this.create_file();
103
await this.extra_setup();
104
}
105
106
private async extra_setup(): Promise<void> {
107
switch (this.param) {
108
case "python":
109
await this.setup_notebook("python3");
110
break;
111
case "jupyter-r":
112
case "r":
113
await this.setup_notebook("ir");
114
break;
115
case "jupyter-bash":
116
await this.setup_notebook("bash");
117
break;
118
case "octave":
119
case "jupyter-octave":
120
await this.setup_notebook("octave");
121
break;
122
}
123
}
124
125
// For some jupyter notebook kernels, initialize them.
126
private async setup_notebook(kernel: Kernel) {
127
if (this.path == null)
128
throw new Error("WelcomeFile::setup_notebook path is not defined");
129
let editor_actions: any;
130
// inspired by nbgrader actions
131
while (true) {
132
editor_actions = redux.getEditorActions(this.project_id, this.path);
133
if (editor_actions != null) break;
134
await delay(200);
135
}
136
137
const jactions = editor_actions.jupyter_actions as JupyterActions;
138
if (jactions.syncdb.get_state() == "init") {
139
await once(jactions.syncdb, "ready");
140
}
141
jactions.set_kernel(kernel);
142
await jactions.save(); // TODO how to make sure get_cell_list() has at least one cell?
143
let cell_id: string = jactions.store.get_cell_list().first();
144
145
WelcomeSetups[kernel].forEach((cell) => {
146
jactions.set_cell_input(cell_id, cell.content);
147
if (cell.type == "markdown") {
148
jactions.set_cell_type(cell_id, "markdown");
149
} else {
150
jactions.run_code_cell(cell_id);
151
}
152
cell_id = jactions.insert_cell_adjacent(cell_id, +1);
153
});
154
}
155
156
// Calling the "create file" action will properly initialize certain files,
157
// in particular .tex
158
private async create_file(): Promise<void> {
159
if (this.path == null)
160
throw new Error("WelcomeFile::create_file – path is not defined");
161
const project_actions = redux.getProjectActions(this.project_id);
162
const { name, ext } = separate_file_extension(this.path);
163
await project_actions.create_file({
164
name,
165
ext,
166
current_path: "",
167
switch_over: true,
168
});
169
}
170
171
// Derive a file from the parameter value, which implies what to show.
172
private make_path(): string | undefined {
173
switch (this.param) {
174
case "ipynb":
175
case "jupyter":
176
case "python":
177
case "true":
178
// TODO expand this first notebook to be a bit more exciting…
179
return "Welcome to CoCalc.ipynb";
180
case "r":
181
case "jupyter-r":
182
case "jupyter-bash":
183
case "octave":
184
case "jupyter-octave":
185
// TODO: pre-select the R, bash or octave kernel
186
return "Welcome to CoCalc.ipynb";
187
case "linux":
188
case "terminal":
189
return "Welcome to CoCalc.term";
190
case "sagews":
191
case "sage":
192
return "Welcome to CoCalc.sagews";
193
case "latex":
194
return "Welcome-to-CoCalc.tex";
195
case "x11":
196
return "Welcome to CoCalc.x11";
197
default:
198
console.warn(`Got unknown param=${this.param}`);
199
return undefined;
200
}
201
}
202
}
203
204