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