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/export/export-assignment.ts
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45/*6Export collected homework assignments in a format that is easy to7use in an external tool that knows nothing about Sage worksheets8or Jupyter notebooks and with the directory structure removed.9In practice, this means that sagews and ipynb files are converted10to pdf, the special txt files indicated the student name are removed,11files in subdirectories are ignored, and filenames are prefixed with12the student name.13*/1415import { endswith, len, startswith } from "@cocalc/util/misc";16import { exec, project_api } from "../../frame-editors/generic/client";17import { StudentsMap } from "../store";1819export async function export_assignment(20project_id: string,21collect_path: string,22export_path: string,23students: StudentsMap,24student_name: Function,25log: Function, // call with a string giving the current thing being done.26): Promise<void> {27log(`Ensure target path "${export_path}" exists`);28await exec({ command: "mkdir", args: ["-p", export_path], project_id });29const errors: { [name: string]: string } = {};3031// for each student, do the export32let n: number = 1;33for (const [student_id, student] of students) {34const name = student_name(student_id);35const desc = "Exporting " + name + ` (student ${n} of ${students.size}): `;36n += 1;37log(desc);38if (student.get("deleted")) continue;39try {40await export_one_directory(41project_id,42collect_path + "/" + student_id,43export_path,44name,45(s) => log(desc + s),46);47} catch (err) {48errors[name] = `${err}`;49}50}5152log("Create zip archive of export directory");53await exec({54command: "zip",55args: ["-r", export_path + ".zip", export_path],56project_id,57});5859if (len(errors) > 0) {60throw Error(JSON.stringify(errors));61}62}6364async function export_one_directory(65project_id: string,66source: string,67target: string,68prefix: string,69log: Function,70): Promise<void> {71const api = await project_api(project_id);72let listing;73try {74listing = await api.listing(source);75} catch (err) {76if (err.toString().indexOf("ENOENT") != -1) {77// ignore completely missing directories -- no problem.78return;79}80}81let x: any;82const timeout = 60; // 60 seconds83for (x of listing) {84if (x.isdir) continue; // we ignore subdirectories...85const { name } = x;86if (startswith(name, "STUDENT")) continue;87if (startswith(name, ".")) continue;88log(name);89if (endswith(name, ".ipynb")) {90// convert, then move html and pdf91const pdf = name.slice(0, name.length - "ipynb".length) + "pdf";92const html = name.slice(0, name.length - "ipynb".length) + "html";93try {94try {95// See packages/frontend/project/websocket/api.ts for jupyter_nbconvert:96await api.jupyter_nbconvert({97args: ["--to", "cocalc-pdf", name],98directory: source,99timeout,100});101} catch (err) {102// even if this fails, html might have been created fine as part of the103// conversion process.104log(`WARNING: conversion to PDF may have failed ${err}`);105}106await exec({107command: "mv",108args: [source + "/" + html, target + "/" + prefix + "-" + html],109project_id,110});111await exec({112command: "mv",113args: [source + "/" + pdf, target + "/" + prefix + "-" + pdf],114project_id,115});116} catch (err) {117log(`WARNING: Conversion ipynb to PDF failed. ${err}`);118}119} else if (endswith(name, ".sagews")) {120try {121// convert, then move pdf122const pdf = name.slice(0, name.length - "sagews".length) + "pdf";123await exec({124command: "cc-sagews2pdf",125args: [source + "/" + name],126project_id,127timeout,128});129await exec({130command: "mv",131args: [source + "/" + pdf, target + "/" + prefix + "-" + pdf],132project_id,133});134} catch (err) {135// Failed to convert sagews to pdf, so do nothing.136log(`WARNING -- problem copying ${name} -- ${err}`);137}138}139try {140// Always copy original file over (failure is NON fatal)141await exec({142command: "cp",143args: [source + "/" + name, target + "/" + prefix + "-" + name],144project_id,145});146} catch (err) {147log(`WARNING -- problem copying ${name} -- ${err}`);148}149}150}151152153