Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/frontend/course/nbgrader/nbgrader-button.tsx
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45/*6Render the nbgrader button at the top of the assignment.7*/89import { Alert, Button, Popconfirm } from "antd";10import { redux, useActions, useRedux } from "../../app-framework";11import { useMemo, useState } from "react";12import { Icon, Gap, Tip } from "../../components";13import { CourseStore, NBgraderRunInfo, PARALLEL_DEFAULT } from "../store";14import { CourseActions } from "../actions";15import { nbgrader_status } from "./util";16import { plural } from "@cocalc/util/misc";17import { webapp_client } from "@cocalc/frontend/webapp-client";1819interface Props {20name: string;21assignment_id: string;22}2324export function NbgraderButton({ name, assignment_id }: Props) {25const actions: undefined | CourseActions = useActions(name);26const nbgrader_run_info: NBgraderRunInfo = useRedux([27name,28"nbgrader_run_info",29]);30const assignment = useRedux([name, "assignments", assignment_id]);31const [show_more_info, set_show_more_info] = useState<boolean>(false);32const settings = useRedux([name, "settings"]);3334const status = useMemo(() => {35const store: undefined | CourseStore = redux.getStore(name) as any;36if (store == null) return;37return nbgrader_status(assignment);38}, [assignment]); // also depends on all student ids, but not worrying about that for now.3940const running = useMemo(() => {41if (nbgrader_run_info == null) return false;42const t = nbgrader_run_info.get(assignment_id);43if (webapp_client.server_time() - (t ?? 0) <= 1000 * 60 * 10) {44// Time starting is set and it's also within the last few minutes.45// This "few minutes" is just in case -- we probably shouldn't need46// that at all ever, but it could make cocalc state usable in case of47// weird issues, I guess). User could also just close and re-open48// the course file, which resets this state completely.49return true;50}51return false;52}, [nbgrader_run_info]);5354function render_parallel() {55const n = settings.get("nbgrader_parallel") ?? PARALLEL_DEFAULT;56return (57<Tip58title={`Nbgrader parallel limit: grade ${n} students at once`}59tip="This is the max number of students to grade in parallel. Change this in course configuration."60>61<div style={{ marginTop: "5px", fontWeight: 400 }}>62Grade up to {n} students at once.63</div>64</Tip>65);66}6768function render_more_info() {69if (status == null) return <span />;70const todo = status.not_attempted + status.failed;71const total = status.attempted + status.not_attempted;72const failed =73status.failed > 0 ? ` ${status.failed} failed autograding.` : "";74const not_attempted =75status.not_attempted > 076? ` ${status.not_attempted} not autograded.`77: "";78return (79<Alert80style={{ marginTop: "5px" }}81type="success"82message={83<span style={{ fontSize: "14px" }}>84Autograded {status.succeeded}/{total} assignments.{failed}85{not_attempted}86</span>87}88description={89<div>90{todo > 0 && (91<span>92<br />93<Button94disabled={running}95type={"primary"}96onClick={() => {97actions?.assignments.run_nbgrader_for_all_students(98assignment_id,99true,100);101}}102>103Autograde {todo} not-graded {plural(todo, "assignment")}104</Button>105</span>106)}107{status.attempted > 0 && (108<span>109<br />110<Popconfirm111title={`Are you sure you want to autograde ALL ${total} ${plural(112total,113"student",114)}?`}115onConfirm={() => {116actions?.assignments.run_nbgrader_for_all_students(117assignment_id,118);119}}120>121<Button122danger123style={{124width: "100%",125overflow: "hidden",126}}127disabled={running}128>129Autograde all {total} {plural(total, "assignment")}...130</Button>131</Popconfirm>132</span>133)}134{render_parallel()}135{actions && (136<SyncGrades137actions={actions}138assignment_id={assignment_id}139running={running}140/>141)}142</div>143}144/>145);146}147148const label = running ? (149<span>150{" "}151<Icon name="cocalc-ring" spin />152<Gap /> Nbgrader is running153</span>154) : (155<span>Nbgrader...</span>156);157return (158<div style={{ margin: "5px 0" }}>159<Button onClick={() => set_show_more_info(!show_more_info)}>160<Icon161style={{ width: "20px" }}162name={show_more_info ? "caret-down" : "caret-right"}163/>164<Gap /> {label}165</Button>166{show_more_info && render_more_info()}167</div>168);169}170171interface SyncGradesProps {172actions: CourseActions;173assignment_id: string;174running: boolean;175}176177function SyncGrades({ actions, assignment_id, running }: SyncGradesProps) {178return (179<Popconfirm180title={`Copy the nbgrader grades to be the assigned grades for all students, even if there are ungraded manual problems, errors or other issues? You probably don't need to do this.`}181onConfirm={() => {182actions.assignments.set_nbgrader_scores_for_all_students({183assignment_id,184force: true,185commit: true,186});187}}188overlayStyle={{ maxWidth: "500px" }}189>190<Button191style={{192marginTop: "10px",193width: "100%",194overflow: "hidden",195}}196disabled={running}197>198Sync grades...199</Button>200</Popconfirm>201);202}203204205