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/course/configuration/nbgrader.tsx
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
import { Checkbox } from "@cocalc/frontend/antd-bootstrap";
7
import {
8
CSS,
9
redux,
10
useActions,
11
useRedux,
12
} from "@cocalc/frontend/app-framework";
13
import { A, Icon } from "@cocalc/frontend/components";
14
import { InputNumber } from "antd";
15
import { SelectProject } from "@cocalc/frontend/projects/select-project";
16
import { Card, Radio } from "antd";
17
import { CourseActions } from "../actions";
18
import {
19
NBGRADER_CELL_TIMEOUT_MS,
20
NBGRADER_MAX_OUTPUT,
21
NBGRADER_MAX_OUTPUT_PER_CELL,
22
NBGRADER_TIMEOUT_MS,
23
} from "../assignments/consts";
24
25
const radioStyle: CSS = {
26
display: "block",
27
whiteSpace: "normal",
28
fontWeight: "inherit",
29
};
30
31
interface Props {
32
name: string;
33
}
34
35
export function Nbgrader({ name }: Props) {
36
const settings = useRedux([name, "settings"]);
37
const course_project_id = useRedux([name, "course_project_id"]);
38
const actions: CourseActions = useActions({ name });
39
if (actions == null) {
40
throw Error("bug");
41
}
42
43
function render_grade_project(): JSX.Element {
44
const location = settings?.get("nbgrader_grade_project")
45
? "project"
46
: "student";
47
return (
48
<div
49
style={{
50
border: "1px solid lightgrey",
51
padding: "10px",
52
borderRadius: "5px",
53
}}
54
>
55
<h6>
56
Where to autograde assignments:{" "}
57
{location == "student"
58
? "in each student's project"
59
: "specific project"}
60
</h6>
61
<Radio.Group
62
onChange={(e) => {
63
if (e.target.value == "student") {
64
actions.configuration.set_nbgrader_grade_project("");
65
} else {
66
actions.configuration.set_nbgrader_grade_project(
67
course_project_id,
68
);
69
}
70
}}
71
value={location}
72
>
73
<Radio value={"student"} key={"student"} style={radioStyle}>
74
Grade assignments in each student's own project
75
</Radio>
76
<Radio value={"project"} key={"project"} style={radioStyle}>
77
Grade assignments in a project of your choice
78
</Radio>
79
</Radio.Group>
80
<br />
81
{location == "project" && (
82
<div>
83
<SelectProject
84
style={{ width: "100%", padding: "5px 25px" }}
85
onChange={actions.configuration.set_nbgrader_grade_project}
86
value={settings?.get("nbgrader_grade_project")}
87
/>
88
{settings?.get("nbgrader_grade_project") &&
89
settings?.get("nbgrader_grade_project") != course_project_id && (
90
<a
91
style={{ marginLeft: "25px" }}
92
onClick={() =>
93
redux.getActions("projects").open_project({
94
project_id: settings?.get("nbgrader_grade_project"),
95
switch_to: true,
96
})
97
}
98
>
99
Open grading project...
100
</a>
101
)}
102
</div>
103
)}
104
<hr />
105
<i>Where to grade:</i> choose the project in which to run autograding.
106
You can create a new project dedicated to running nbgrader, upgrade or
107
license it appropriately, and copy any files to it that student work
108
depends on. This new project will be shared will all collaborators of
109
this instructorproject.
110
<br />
111
You can also grade all student work in the student's own project, which
112
is good because the code runs in the same environment as the student
113
work (and won't harm any files you have), but can be slower since each
114
student project has to start running.
115
</div>
116
);
117
}
118
119
function render_include_hidden_tests(): JSX.Element {
120
return (
121
<div
122
style={{
123
border: "1px solid lightgrey",
124
padding: "10px",
125
borderRadius: "5px",
126
}}
127
>
128
<h6>
129
Nbgrader hidden tests:{" "}
130
{settings?.get("nbgrader_include_hidden_tests")
131
? "Included"
132
: "NOT included"}
133
</h6>
134
<Checkbox
135
checked={settings?.get("nbgrader_include_hidden_tests")}
136
onChange={(e) =>
137
actions.configuration.set_nbgrader_include_hidden_tests(
138
(e.target as any).checked,
139
)
140
}
141
>
142
<i>Include the hidden tests:</i> Select this if you want the notebook
143
to contain why answers failed your hidden tests. The drawback is that
144
if you return assignments to your students, then you will reveal all
145
the hidden tests to the students.
146
</Checkbox>
147
</div>
148
);
149
}
150
151
function render_timeouts(): JSX.Element {
152
const timeout = Math.round(
153
settings.get("nbgrader_timeout_ms", NBGRADER_TIMEOUT_MS) / 1000,
154
);
155
const cell_timeout = Math.round(
156
settings.get("nbgrader_cell_timeout_ms", NBGRADER_CELL_TIMEOUT_MS) / 1000,
157
);
158
return (
159
<div
160
style={{
161
border: "1px solid lightgrey",
162
padding: "10px",
163
borderRadius: "5px",
164
}}
165
>
166
<h6>Nbgrader timeouts: {timeout} seconds</h6>
167
<i>Grading timeout in seconds:</i> if grading a student notebook takes
168
longer than <i>{timeout} seconds</i>, then it is terminated with a
169
timeout error.
170
<InputNumber
171
onChange={(n) =>
172
actions.configuration.set_nbgrader_timeout_ms(
173
n ? n * 1000 : undefined,
174
)
175
}
176
min={30}
177
max={3600}
178
value={timeout}
179
/>
180
<br />
181
<i>Cell grading timeout in seconds:</i> if grading a cell in a student
182
notebook takes longer than <i>{cell_timeout} seconds</i>, then that cell
183
is terminated with a timeout error.
184
<InputNumber
185
onChange={(n) =>
186
actions.configuration.set_nbgrader_cell_timeout_ms(
187
n ? Math.min(n * 1000, timeout * 1000) : undefined,
188
)
189
}
190
min={5}
191
max={3600}
192
value={cell_timeout}
193
/>
194
</div>
195
);
196
}
197
198
function render_limits(): JSX.Element {
199
const max_output = Math.round(
200
settings.get("nbgrader_max_output", NBGRADER_MAX_OUTPUT),
201
);
202
const max_output_per_cell = Math.round(
203
settings.get(
204
"nbgrader_max_output_per_cell",
205
NBGRADER_MAX_OUTPUT_PER_CELL,
206
),
207
);
208
return (
209
<div
210
style={{
211
border: "1px solid lightgrey",
212
padding: "10px",
213
borderRadius: "5px",
214
}}
215
>
216
<h6>Nbgrader output limits: {Math.round(max_output / 1000)} KB</h6>
217
<i>Max output:</i> if total output from all cells exceeds{" "}
218
{Math.round(max_output / 1000)} KB, then further output is truncated.
219
<InputNumber
220
onChange={(n) =>
221
actions.configuration.set_nbgrader_max_output(
222
n ? n * 1000 : undefined,
223
)
224
}
225
min={1}
226
max={10000}
227
value={Math.round(max_output / 1000)}
228
/>
229
<br />
230
<i>Max output per cell:</i> if output from a cell exceeds{" "}
231
{Math.round(max_output_per_cell / 1000)} KB, then further output is
232
truncated.
233
<InputNumber
234
onChange={(n) =>
235
actions.configuration.set_nbgrader_max_output_per_cell(
236
n ? n * 1000 : undefined,
237
)
238
}
239
min={1}
240
max={10000}
241
value={Math.round(max_output_per_cell / 1000)}
242
/>
243
</div>
244
);
245
}
246
247
function render_parallel(): JSX.Element {
248
const parallel = Math.round(
249
settings.get("nbgrader_parallel") ??
250
actions.get_store().get_nbgrader_parallel(),
251
);
252
return (
253
<div
254
style={{
255
border: "1px solid lightgrey",
256
padding: "10px",
257
borderRadius: "5px",
258
}}
259
>
260
<h6>
261
Nbgrader parallel limit:{" "}
262
{parallel > 1
263
? `grade ${parallel} students at once`
264
: "one student a time"}
265
</h6>
266
<i>Max number of students</i> to grade in parallel. What is optimal
267
could depend on where grading is happening (see "Where to autograde
268
assignments" above), and compute resources you or your students have
269
bought.
270
<InputNumber
271
onChange={(n) =>
272
actions.configuration.set_nbgrader_parallel(n ? n : undefined)
273
}
274
min={1}
275
max={50}
276
value={parallel}
277
/>
278
</div>
279
);
280
}
281
282
return (
283
<Card
284
title={
285
<A href="https://doc.cocalc.com/teaching-nbgrader.html">
286
<Icon name="graduation-cap" /> Nbgrader
287
</A>
288
}
289
>
290
{render_grade_project()}
291
<br />
292
{render_include_hidden_tests()}
293
<br />
294
{render_timeouts()}
295
<br />
296
{render_limits()}
297
<br />
298
{render_parallel()}
299
</Card>
300
);
301
}
302
303