Path: blob/master/src/packages/frontend/course/configuration/nbgrader.tsx
5965 views
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { Card, InputNumber, Radio } from "antd";67import { Checkbox } from "@cocalc/frontend/antd-bootstrap";8import {9CSS,10redux,11useActions,12useRedux,13} from "@cocalc/frontend/app-framework";14import { A, Icon } from "@cocalc/frontend/components";15import { SelectProject } from "@cocalc/frontend/projects/select-project";1617import { CourseActions } from "../actions";18import {19NBGRADER_CELL_TIMEOUT_MS,20NBGRADER_MAX_OUTPUT,21NBGRADER_MAX_OUTPUT_PER_CELL,22NBGRADER_TIMEOUT_MS,23} from "../assignments/consts";2425const radioStyle: CSS = {26display: "block",27whiteSpace: "normal",28fontWeight: "inherit",29} as const;3031interface Props {32name: string;33}3435export function Nbgrader({ name }: Props) {36const settings = useRedux([name, "settings"]);37const course_project_id = useRedux([name, "course_project_id"]);38const actions: CourseActions = useActions({ name });39if (actions == null) {40throw Error("bug");41}4243function render_grade_project(): React.JSX.Element {44const location = settings?.get("nbgrader_grade_project")45? "project"46: "student";47return (48<div49style={{50border: "1px solid lightgrey",51padding: "10px",52borderRadius: "5px",53}}54>55<h6>56Where to autograde assignments:{" "}57{location == "student"58? "in each student's project"59: "specific project"}60</h6>61<Radio.Group62onChange={(e) => {63if (e.target.value == "student") {64actions.configuration.set_nbgrader_grade_project("");65} else {66actions.configuration.set_nbgrader_grade_project(67course_project_id,68);69}70}}71value={location}72>73<Radio value={"student"} key={"student"} style={radioStyle}>74Grade assignments in each student's own project75</Radio>76<Radio value={"project"} key={"project"} style={radioStyle}>77Grade assignments in a project of your choice78</Radio>79</Radio.Group>80<br />81{location == "project" && (82<div>83<SelectProject84style={{ width: "100%", padding: "5px 25px" }}85onChange={actions.configuration.set_nbgrader_grade_project}86value={settings?.get("nbgrader_grade_project")}87/>88{settings?.get("nbgrader_grade_project") &&89settings?.get("nbgrader_grade_project") != course_project_id && (90<a91style={{ marginLeft: "25px" }}92onClick={() =>93redux.getActions("projects").open_project({94project_id: settings?.get("nbgrader_grade_project"),95switch_to: true,96})97}98>99Open grading project...100</a>101)}102</div>103)}104<hr />105<i>Where to grade:</i> choose the project in which to run autograding.106You can create a new project dedicated to running nbgrader, upgrade or107license it appropriately, and copy any files to it that student work108depends on. This new project will be shared will all collaborators of109this instructor project.110<br />111You can also grade all student work in the student's own project, which112is good because the code runs in the same environment as the student113work (and won't harm any files you have), but can be slower since each114student project has to start running.115</div>116);117}118119function render_include_hidden_tests(): React.JSX.Element {120return (121<div122style={{123border: "1px solid lightgrey",124padding: "10px",125borderRadius: "5px",126}}127>128<h6>129nbgrader hidden tests:{" "}130{settings?.get("nbgrader_include_hidden_tests")131? "Included"132: "NOT included"}133</h6>134<Checkbox135checked={settings?.get("nbgrader_include_hidden_tests")}136onChange={(e) =>137actions.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 notebook143to contain why answers failed your hidden tests. The drawback is that144if you return assignments to your students, then you will reveal all145the hidden tests to the students.146</Checkbox>147</div>148);149}150151function render_timeouts(): React.JSX.Element {152const timeout = Math.round(153settings.get("nbgrader_timeout_ms", NBGRADER_TIMEOUT_MS) / 1000,154);155const cell_timeout = Math.round(156settings.get("nbgrader_cell_timeout_ms", NBGRADER_CELL_TIMEOUT_MS) / 1000,157);158return (159<div160style={{161border: "1px solid lightgrey",162padding: "10px",163borderRadius: "5px",164}}165>166<h6>nbgrader timeouts: {timeout} seconds</h6>167<i>Grading timeout in seconds:</i> if grading a student notebook takes168longer than <i>{timeout} seconds</i>, then it is terminated with a169timeout error.170<InputNumber171onChange={(n) =>172actions.configuration.set_nbgrader_timeout_ms(173n ? n * 1000 : undefined,174)175}176min={30}177max={3600}178value={timeout}179/>180<br />181<i>Cell grading timeout in seconds:</i> if grading a cell in a student182notebook takes longer than <i>{cell_timeout} seconds</i>, then that cell183is terminated with a timeout error.184<InputNumber185onChange={(n) =>186actions.configuration.set_nbgrader_cell_timeout_ms(187n ? Math.min(n * 1000, timeout * 1000) : undefined,188)189}190min={5}191max={3600}192value={cell_timeout}193/>194</div>195);196}197198function render_limits(): React.JSX.Element {199const max_output = Math.round(200settings.get("nbgrader_max_output", NBGRADER_MAX_OUTPUT),201);202const max_output_per_cell = Math.round(203settings.get(204"nbgrader_max_output_per_cell",205NBGRADER_MAX_OUTPUT_PER_CELL,206),207);208return (209<div210style={{211border: "1px solid lightgrey",212padding: "10px",213borderRadius: "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<InputNumber220onChange={(n) =>221actions.configuration.set_nbgrader_max_output(222n ? n * 1000 : undefined,223)224}225min={1}226max={10000}227value={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 is232truncated.233<InputNumber234onChange={(n) =>235actions.configuration.set_nbgrader_max_output_per_cell(236n ? n * 1000 : undefined,237)238}239min={1}240max={10000}241value={Math.round(max_output_per_cell / 1000)}242/>243</div>244);245}246247function render_parallel(): React.JSX.Element {248const parallel = Math.round(249settings.get("nbgrader_parallel") ??250actions.get_store().get_nbgrader_parallel(),251);252return (253<div254style={{255border: "1px solid lightgrey",256padding: "10px",257borderRadius: "5px",258}}259>260<h6>261nbgrader parallel limit:{" "}262{parallel > 1263? `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 optimal267could depend on where grading is happening (see "Where to autograde268assignments" above), and compute resources you or your students have269bought.270<InputNumber271onChange={(n) =>272actions.configuration.set_nbgrader_parallel(n ? n : undefined)273}274min={1}275max={50}276value={parallel}277/>278</div>279);280}281282return (283<Card284title={285<>286<Icon name="graduation-cap" /> nbgrader (287<A href="https://doc.cocalc.com/teaching-nbgrader.html">Docs</A>)288</>289}290>291{render_grade_project()}292<br />293{render_include_hidden_tests()}294<br />295{render_timeouts()}296<br />297{render_limits()}298<br />299{render_parallel()}300</Card>301);302}303304305