canvaslms-cli 1.6.2 → 1.6.4-fix
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +64 -31
- package/README.md +131 -129
- package/dist/commands/announcements.d.ts +2 -2
- package/dist/commands/announcements.d.ts.map +1 -1
- package/dist/commands/announcements.js +29 -20
- package/dist/commands/announcements.js.map +1 -1
- package/dist/commands/api.d.ts +1 -1
- package/dist/commands/api.d.ts.map +1 -1
- package/dist/commands/api.js +1 -1
- package/dist/commands/api.js.map +1 -1
- package/dist/commands/assignments.d.ts +2 -2
- package/dist/commands/assignments.d.ts.map +1 -1
- package/dist/commands/assignments.js +24 -19
- package/dist/commands/assignments.js.map +1 -1
- package/dist/commands/calendar.d.ts +7 -0
- package/dist/commands/calendar.d.ts.map +1 -0
- package/dist/commands/calendar.js +150 -0
- package/dist/commands/calendar.js.map +1 -0
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +102 -85
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/grades.d.ts +2 -2
- package/dist/commands/grades.d.ts.map +1 -1
- package/dist/commands/grades.js +296 -176
- package/dist/commands/grades.js.map +1 -1
- package/dist/commands/list.d.ts +1 -1
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/list.js +21 -19
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/modules.d.ts +5 -0
- package/dist/commands/modules.d.ts.map +1 -0
- package/dist/commands/modules.js +263 -0
- package/dist/commands/modules.js.map +1 -0
- package/dist/commands/profile.d.ts.map +1 -1
- package/dist/commands/profile.js +57 -32
- package/dist/commands/profile.js.map +1 -1
- package/dist/commands/submit.d.ts +1 -2
- package/dist/commands/submit.d.ts.map +1 -1
- package/dist/commands/submit.js +88 -79
- package/dist/commands/submit.js.map +1 -1
- package/dist/index.d.ts +16 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -14
- package/dist/index.js.map +1 -1
- package/dist/lib/api-client.d.ts +3 -0
- package/dist/lib/api-client.d.ts.map +1 -1
- package/dist/lib/api-client.js +31 -16
- package/dist/lib/api-client.js.map +1 -1
- package/dist/lib/config-validator.d.ts.map +1 -1
- package/dist/lib/config-validator.js +12 -12
- package/dist/lib/config-validator.js.map +1 -1
- package/dist/lib/config.d.ts +1 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +16 -14
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/display.d.ts +10 -4
- package/dist/lib/display.d.ts.map +1 -1
- package/dist/lib/display.js +263 -167
- package/dist/lib/display.js.map +1 -1
- package/dist/lib/file-upload.d.ts.map +1 -1
- package/dist/lib/file-upload.js +17 -17
- package/dist/lib/file-upload.js.map +1 -1
- package/dist/lib/interactive.d.ts +1 -1
- package/dist/lib/interactive.d.ts.map +1 -1
- package/dist/lib/interactive.js +196 -144
- package/dist/lib/interactive.js.map +1 -1
- package/dist/src/index.d.ts +0 -1
- package/dist/src/index.js +83 -63
- package/dist/src/index.js.map +1 -1
- package/dist/types/index.d.ts +34 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +10 -3
- package/dist/commands/coursenames.d.ts +0 -15
- package/dist/commands/coursenames.d.ts.map +0 -1
- package/dist/commands/coursenames.js +0 -150
- package/dist/commands/coursenames.js.map +0 -1
- package/dist/lib/course-utils.d.ts +0 -6
- package/dist/lib/course-utils.d.ts.map +0 -1
- package/dist/lib/course-utils.js +0 -17
- package/dist/lib/course-utils.js.map +0 -1
package/dist/lib/display.js
CHANGED
|
@@ -1,53 +1,60 @@
|
|
|
1
|
-
import chalk from
|
|
2
|
-
import { makeCanvasRequest } from
|
|
3
|
-
import { createReadlineInterface, askQuestion } from
|
|
4
|
-
export function pad(str, len, align =
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { makeCanvasRequest } from "./api-client.js";
|
|
3
|
+
import { createReadlineInterface, askQuestion } from "./interactive.js";
|
|
4
|
+
export function pad(str, len, align = "left") {
|
|
5
5
|
const strLen = stripAnsi(str).length;
|
|
6
6
|
const padding = Math.max(0, len - strLen);
|
|
7
|
-
if (align ===
|
|
8
|
-
return
|
|
7
|
+
if (align === "right") {
|
|
8
|
+
return " ".repeat(padding) + str;
|
|
9
9
|
}
|
|
10
|
-
else if (align ===
|
|
10
|
+
else if (align === "center") {
|
|
11
11
|
const leftPad = Math.floor(padding / 2);
|
|
12
12
|
const rightPad = padding - leftPad;
|
|
13
|
-
return
|
|
13
|
+
return " ".repeat(leftPad) + str + " ".repeat(rightPad);
|
|
14
14
|
}
|
|
15
|
-
return str +
|
|
15
|
+
return str + " ".repeat(padding);
|
|
16
16
|
}
|
|
17
17
|
function stripAnsi(str) {
|
|
18
|
-
return str.replace(/\x1B\[[0-9;]*[a-zA-Z]/g,
|
|
18
|
+
return str.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "");
|
|
19
19
|
}
|
|
20
20
|
export function truncate(str, maxLen) {
|
|
21
|
-
|
|
21
|
+
const visible = stripAnsi(str);
|
|
22
|
+
if (visible.length <= maxLen)
|
|
22
23
|
return str;
|
|
23
|
-
|
|
24
|
+
if (maxLen <= 3)
|
|
25
|
+
return visible.substring(0, maxLen);
|
|
26
|
+
const truncatedVisible = visible.substring(0, Math.max(0, maxLen - 3)) + "...";
|
|
27
|
+
return truncatedVisible;
|
|
24
28
|
}
|
|
25
29
|
function calculateColumnWidths(columns, data, terminalWidth, showRowNumbers) {
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const
|
|
30
|
+
const numCols = columns.length;
|
|
31
|
+
const borderOverhead = 2 * numCols + 1;
|
|
32
|
+
const rowNumWidth = showRowNumbers
|
|
33
|
+
? Math.max(3, String(data.length).length + 1)
|
|
34
|
+
: 0;
|
|
35
|
+
const rowNumOverhead = showRowNumbers ? 2 + rowNumWidth : 0;
|
|
36
|
+
const effectiveTermWidth = Math.min(terminalWidth, 240);
|
|
37
|
+
const availableWidth = Math.max(numCols * 4, effectiveTermWidth - borderOverhead - rowNumOverhead);
|
|
38
|
+
let widths = [];
|
|
32
39
|
const contentWidths = [];
|
|
33
40
|
let fixedWidthTotal = 0;
|
|
34
41
|
let totalFlex = 0;
|
|
35
42
|
columns.forEach((col, i) => {
|
|
36
|
-
const headerLen = col.header.length;
|
|
37
|
-
const maxContentLen = Math.max(headerLen, ...data.map(row => String(row[col.key]
|
|
43
|
+
const headerLen = stripAnsi(col.header).length;
|
|
44
|
+
const maxContentLen = Math.max(headerLen, ...data.map((row) => stripAnsi(String(row[col.key] ?? "")).length));
|
|
38
45
|
contentWidths[i] = maxContentLen;
|
|
39
46
|
if (col.width) {
|
|
40
47
|
widths[i] = col.width;
|
|
41
48
|
fixedWidthTotal += col.width;
|
|
42
49
|
}
|
|
43
50
|
else if (col.flex) {
|
|
44
|
-
const minW = col.minWidth ||
|
|
51
|
+
const minW = col.minWidth || 4;
|
|
45
52
|
widths[i] = minW;
|
|
46
53
|
fixedWidthTotal += minW;
|
|
47
54
|
totalFlex += col.flex;
|
|
48
55
|
}
|
|
49
56
|
else {
|
|
50
|
-
const minW = col.minWidth ||
|
|
57
|
+
const minW = col.minWidth || 4;
|
|
51
58
|
const maxW = col.maxWidth || maxContentLen;
|
|
52
59
|
widths[i] = Math.min(maxW, Math.max(minW, maxContentLen));
|
|
53
60
|
fixedWidthTotal += widths[i];
|
|
@@ -58,18 +65,25 @@ function calculateColumnWidths(columns, data, terminalWidth, showRowNumbers) {
|
|
|
58
65
|
columns.forEach((col, i) => {
|
|
59
66
|
if (col.flex) {
|
|
60
67
|
const extraWidth = Math.floor((remainingWidth * col.flex) / totalFlex);
|
|
61
|
-
const maxW = col.maxWidth ||
|
|
68
|
+
const maxW = col.maxWidth || 999;
|
|
62
69
|
widths[i] = Math.min((widths[i] ?? 0) + extraWidth, maxW);
|
|
63
70
|
}
|
|
64
71
|
});
|
|
65
72
|
}
|
|
66
73
|
let totalWidth = widths.reduce((sum, w) => sum + (w ?? 0), 0);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
74
|
+
while (totalWidth > availableWidth && totalWidth > numCols * 4) {
|
|
75
|
+
let maxIdx = 0;
|
|
76
|
+
let maxW = widths[0] ?? 0;
|
|
77
|
+
for (let i = 1; i < widths.length; i++) {
|
|
78
|
+
if ((widths[i] ?? 0) > maxW) {
|
|
79
|
+
maxW = widths[i] ?? 0;
|
|
80
|
+
maxIdx = i;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (maxW <= 4)
|
|
84
|
+
break;
|
|
85
|
+
widths[maxIdx] = Math.max(4, maxW - 1);
|
|
86
|
+
totalWidth--;
|
|
73
87
|
}
|
|
74
88
|
return widths;
|
|
75
89
|
}
|
|
@@ -78,13 +92,17 @@ export class Table {
|
|
|
78
92
|
data;
|
|
79
93
|
options;
|
|
80
94
|
rowNumWidth = 0;
|
|
95
|
+
lastLineCount = 0;
|
|
96
|
+
lastTableWidth = 0;
|
|
97
|
+
resizeHandler = null;
|
|
98
|
+
isWatching = false;
|
|
81
99
|
constructor(columns, options = {}) {
|
|
82
100
|
this.columns = columns;
|
|
83
101
|
this.data = [];
|
|
84
102
|
this.options = {
|
|
85
103
|
showRowNumbers: true,
|
|
86
|
-
rowNumberHeader:
|
|
87
|
-
...options
|
|
104
|
+
rowNumberHeader: "#",
|
|
105
|
+
...options,
|
|
88
106
|
};
|
|
89
107
|
}
|
|
90
108
|
addRow(row) {
|
|
@@ -100,8 +118,38 @@ export class Table {
|
|
|
100
118
|
}
|
|
101
119
|
renderWithResize() {
|
|
102
120
|
this.renderTable();
|
|
121
|
+
this.startWatching();
|
|
122
|
+
}
|
|
123
|
+
startWatching() {
|
|
124
|
+
if (this.isWatching || !process.stdout.isTTY)
|
|
125
|
+
return;
|
|
126
|
+
this.resizeHandler = () => {
|
|
127
|
+
this.clearTable();
|
|
128
|
+
this.renderTable();
|
|
129
|
+
};
|
|
130
|
+
process.stdout.on("resize", this.resizeHandler);
|
|
131
|
+
this.isWatching = true;
|
|
103
132
|
}
|
|
104
133
|
stopWatching() {
|
|
134
|
+
if (!this.isWatching || !this.resizeHandler)
|
|
135
|
+
return;
|
|
136
|
+
process.stdout.removeListener("resize", this.resizeHandler);
|
|
137
|
+
this.resizeHandler = null;
|
|
138
|
+
this.isWatching = false;
|
|
139
|
+
}
|
|
140
|
+
clearTable() {
|
|
141
|
+
if (this.lastLineCount <= 0)
|
|
142
|
+
return;
|
|
143
|
+
const maxPossibleWraps = Math.ceil(this.lastTableWidth / 20);
|
|
144
|
+
const worstCaseLines = this.lastLineCount * Math.max(maxPossibleWraps, 3);
|
|
145
|
+
const linesToClear = Math.max(worstCaseLines, 60);
|
|
146
|
+
process.stdout.write("\x1b[2K");
|
|
147
|
+
for (let i = 0; i < linesToClear; i++) {
|
|
148
|
+
process.stdout.write("\x1b[1A");
|
|
149
|
+
process.stdout.write("\x1b[2K");
|
|
150
|
+
}
|
|
151
|
+
process.stdout.write("\x1b[G");
|
|
152
|
+
process.stdout.write("\x1b[0J");
|
|
105
153
|
}
|
|
106
154
|
renderTable() {
|
|
107
155
|
const terminalWidth = process.stdout.columns || 100;
|
|
@@ -109,50 +157,78 @@ export class Table {
|
|
|
109
157
|
if (this.options.showRowNumbers) {
|
|
110
158
|
this.rowNumWidth = Math.max(3, String(this.data.length).length + 1);
|
|
111
159
|
}
|
|
160
|
+
const numCols = this.columns.length;
|
|
161
|
+
const borderWidth = 2 * numCols + 1;
|
|
162
|
+
const rowNumExtra = this.options.showRowNumbers ? 2 + this.rowNumWidth : 0;
|
|
163
|
+
const tableWidth = widths.reduce((a, b) => a + b, 0) + borderWidth + rowNumExtra;
|
|
164
|
+
this.lastTableWidth = tableWidth;
|
|
165
|
+
const linesPerRow = Math.max(1, Math.ceil(tableWidth / terminalWidth));
|
|
166
|
+
let lineCount = 0;
|
|
112
167
|
if (this.options.title) {
|
|
113
168
|
console.log(chalk.cyan.bold(`\n${this.options.title}`));
|
|
169
|
+
lineCount += 2;
|
|
114
170
|
}
|
|
115
171
|
this.renderTopBorder(widths);
|
|
172
|
+
lineCount += linesPerRow;
|
|
116
173
|
this.renderHeader(widths);
|
|
174
|
+
lineCount += linesPerRow;
|
|
117
175
|
this.renderHeaderSeparator(widths);
|
|
118
|
-
|
|
176
|
+
lineCount += linesPerRow;
|
|
177
|
+
this.data.forEach((row, index) => {
|
|
178
|
+
this.renderRow(row, index, widths);
|
|
179
|
+
lineCount += linesPerRow;
|
|
180
|
+
});
|
|
119
181
|
this.renderBottomBorder(widths);
|
|
182
|
+
lineCount += linesPerRow;
|
|
183
|
+
this.lastLineCount = lineCount;
|
|
120
184
|
}
|
|
121
185
|
renderTopBorder(widths) {
|
|
122
|
-
let border = chalk.gray(
|
|
186
|
+
let border = chalk.gray("╭─");
|
|
123
187
|
if (this.options.showRowNumbers) {
|
|
124
|
-
border += chalk.gray(
|
|
188
|
+
border += chalk.gray("─".repeat(this.rowNumWidth)) + chalk.gray("┬─");
|
|
125
189
|
}
|
|
126
|
-
border += widths
|
|
127
|
-
|
|
190
|
+
border += widths
|
|
191
|
+
.map((w) => chalk.gray("─".repeat(w)))
|
|
192
|
+
.join(chalk.gray("┬─"));
|
|
193
|
+
border += chalk.gray("╮");
|
|
128
194
|
console.log(border);
|
|
129
195
|
}
|
|
130
196
|
renderHeader(widths) {
|
|
131
|
-
let header = chalk.gray(
|
|
197
|
+
let header = chalk.gray("│ ");
|
|
132
198
|
if (this.options.showRowNumbers) {
|
|
133
|
-
header +=
|
|
199
|
+
header +=
|
|
200
|
+
chalk.cyan.bold(pad(truncate(this.options.rowNumberHeader, this.rowNumWidth), this.rowNumWidth)) + chalk.gray("│ ");
|
|
134
201
|
}
|
|
135
|
-
header += this.columns
|
|
136
|
-
|
|
202
|
+
header += this.columns
|
|
203
|
+
.map((col, i) => {
|
|
204
|
+
const colWidth = widths[i] || 10;
|
|
205
|
+
return chalk.cyan.bold(pad(truncate(col.header, colWidth), colWidth, col.align));
|
|
206
|
+
})
|
|
207
|
+
.join(chalk.gray("│ "));
|
|
208
|
+
header += chalk.gray("│");
|
|
137
209
|
console.log(header);
|
|
138
210
|
}
|
|
139
211
|
renderHeaderSeparator(widths) {
|
|
140
|
-
let separator = chalk.gray(
|
|
212
|
+
let separator = chalk.gray("├─");
|
|
141
213
|
if (this.options.showRowNumbers) {
|
|
142
|
-
separator += chalk.gray(
|
|
214
|
+
separator += chalk.gray("─".repeat(this.rowNumWidth)) + chalk.gray("┼─");
|
|
143
215
|
}
|
|
144
|
-
separator += widths
|
|
145
|
-
|
|
216
|
+
separator += widths
|
|
217
|
+
.map((w) => chalk.gray("─".repeat(w)))
|
|
218
|
+
.join(chalk.gray("┼─"));
|
|
219
|
+
separator += chalk.gray("┤");
|
|
146
220
|
console.log(separator);
|
|
147
221
|
}
|
|
148
222
|
renderRow(row, index, widths) {
|
|
149
|
-
let rowStr = chalk.gray(
|
|
223
|
+
let rowStr = chalk.gray("│ ");
|
|
150
224
|
if (this.options.showRowNumbers) {
|
|
151
|
-
rowStr +=
|
|
225
|
+
rowStr +=
|
|
226
|
+
chalk.white(pad(`${index + 1}.`, this.rowNumWidth)) + chalk.gray("│ ");
|
|
152
227
|
}
|
|
153
|
-
rowStr += this.columns
|
|
228
|
+
rowStr += this.columns
|
|
229
|
+
.map((col, i) => {
|
|
154
230
|
const colWidth = widths[i] || 10;
|
|
155
|
-
let value = String(row[col.key] ??
|
|
231
|
+
let value = String(row[col.key] ?? "");
|
|
156
232
|
value = truncate(value, colWidth);
|
|
157
233
|
value = pad(value, colWidth, col.align);
|
|
158
234
|
if (col.color) {
|
|
@@ -162,70 +238,72 @@ export class Table {
|
|
|
162
238
|
value = chalk.white(value);
|
|
163
239
|
}
|
|
164
240
|
return value;
|
|
165
|
-
})
|
|
166
|
-
|
|
241
|
+
})
|
|
242
|
+
.join(chalk.gray("│ "));
|
|
243
|
+
rowStr += chalk.gray("│");
|
|
167
244
|
console.log(rowStr);
|
|
168
245
|
}
|
|
169
246
|
renderBottomBorder(widths) {
|
|
170
|
-
let border = chalk.gray(
|
|
247
|
+
let border = chalk.gray("╰─");
|
|
171
248
|
if (this.options.showRowNumbers) {
|
|
172
|
-
border += chalk.gray(
|
|
249
|
+
border += chalk.gray("─".repeat(this.rowNumWidth)) + chalk.gray("┴─");
|
|
173
250
|
}
|
|
174
|
-
border += widths
|
|
175
|
-
|
|
251
|
+
border += widths
|
|
252
|
+
.map((w) => chalk.gray("─".repeat(w)))
|
|
253
|
+
.join(chalk.gray("┴─"));
|
|
254
|
+
border += chalk.gray("╯");
|
|
176
255
|
console.log(border);
|
|
177
256
|
}
|
|
178
257
|
}
|
|
179
258
|
export function displayCourses(courses, options = {}) {
|
|
180
259
|
const columns = [
|
|
181
|
-
{ key:
|
|
260
|
+
{ key: "name", header: "Course Name", flex: 1, minWidth: 15 },
|
|
182
261
|
];
|
|
183
262
|
if (options.showId) {
|
|
184
|
-
columns.push({ key:
|
|
263
|
+
columns.push({ key: "id", header: "ID", minWidth: 5, maxWidth: 10 });
|
|
185
264
|
}
|
|
186
265
|
const table = new Table(columns);
|
|
187
|
-
courses.forEach(course => {
|
|
266
|
+
courses.forEach((course) => {
|
|
188
267
|
table.addRow({ name: course.name, id: course.id });
|
|
189
268
|
});
|
|
190
269
|
table.render();
|
|
191
270
|
}
|
|
192
271
|
export async function pickCourse(options = {}) {
|
|
193
272
|
const rl = createReadlineInterface();
|
|
194
|
-
console.log(chalk.cyan.bold(options.title ||
|
|
195
|
-
const queryParams = [
|
|
196
|
-
|
|
197
|
-
'include[]=favorites'
|
|
198
|
-
];
|
|
199
|
-
const courses = await makeCanvasRequest('get', 'courses', queryParams);
|
|
273
|
+
console.log(chalk.cyan.bold(options.title || "\nLoading your courses, please wait..."));
|
|
274
|
+
const queryParams = ["enrollment_state=active", "include[]=favorites"];
|
|
275
|
+
const courses = await makeCanvasRequest("get", "courses", queryParams);
|
|
200
276
|
if (!courses || courses.length === 0) {
|
|
201
|
-
console.log(chalk.red(
|
|
277
|
+
console.log(chalk.red("Error: No courses found."));
|
|
202
278
|
rl.close();
|
|
203
279
|
return null;
|
|
204
280
|
}
|
|
205
281
|
let displayCourses = courses;
|
|
206
282
|
if (!options.showAll) {
|
|
207
|
-
const starred = courses.filter(c => c.is_favorite);
|
|
283
|
+
const starred = courses.filter((c) => c.is_favorite);
|
|
208
284
|
if (starred.length > 0) {
|
|
209
285
|
displayCourses = starred;
|
|
210
286
|
}
|
|
211
287
|
}
|
|
212
288
|
console.log(chalk.green(`✓ Found ${displayCourses.length} course(s).`));
|
|
213
289
|
const table = new Table([
|
|
214
|
-
{ key:
|
|
290
|
+
{ key: "name", header: "Course Name", flex: 1, minWidth: 15 },
|
|
215
291
|
]);
|
|
216
|
-
displayCourses.forEach(course => table.addRow({ name: course.name }));
|
|
292
|
+
displayCourses.forEach((course) => table.addRow({ name: course.name }));
|
|
217
293
|
table.renderWithResize();
|
|
218
|
-
const prompt = options.prompt || chalk.bold.cyan(
|
|
294
|
+
const prompt = options.prompt || chalk.bold.cyan("\nEnter course number: ");
|
|
219
295
|
const courseChoice = await askQuestion(rl, prompt);
|
|
220
296
|
table.stopWatching();
|
|
221
|
-
if (!courseChoice.trim() ||
|
|
222
|
-
|
|
297
|
+
if (!courseChoice.trim() ||
|
|
298
|
+
courseChoice === ".." ||
|
|
299
|
+
courseChoice.toLowerCase() === "back") {
|
|
300
|
+
console.log(chalk.red("No course selected. Exiting..."));
|
|
223
301
|
rl.close();
|
|
224
302
|
return null;
|
|
225
303
|
}
|
|
226
304
|
const courseIndex = parseInt(courseChoice) - 1;
|
|
227
305
|
if (courseIndex < 0 || courseIndex >= displayCourses.length) {
|
|
228
|
-
console.log(chalk.red(
|
|
306
|
+
console.log(chalk.red("Invalid course selection."));
|
|
229
307
|
rl.close();
|
|
230
308
|
return null;
|
|
231
309
|
}
|
|
@@ -234,8 +312,12 @@ export async function pickCourse(options = {}) {
|
|
|
234
312
|
return { course: selectedCourse, rl };
|
|
235
313
|
}
|
|
236
314
|
export function formatGrade(submission, pointsPossible) {
|
|
237
|
-
if (submission &&
|
|
238
|
-
|
|
315
|
+
if (submission &&
|
|
316
|
+
submission.score !== null &&
|
|
317
|
+
submission.score !== undefined) {
|
|
318
|
+
const score = submission.score % 1 === 0
|
|
319
|
+
? Math.round(submission.score)
|
|
320
|
+
: submission.score;
|
|
239
321
|
const text = `${score}/${pointsPossible}`;
|
|
240
322
|
const percentage = pointsPossible > 0 ? (score / pointsPossible) * 100 : 0;
|
|
241
323
|
let color = chalk.white;
|
|
@@ -248,56 +330,64 @@ export function formatGrade(submission, pointsPossible) {
|
|
|
248
330
|
return { text, color };
|
|
249
331
|
}
|
|
250
332
|
else if (submission && submission.excused) {
|
|
251
|
-
return { text:
|
|
333
|
+
return { text: "Excused", color: chalk.blue };
|
|
252
334
|
}
|
|
253
335
|
else if (submission && submission.missing) {
|
|
254
|
-
return { text:
|
|
336
|
+
return { text: "Missing", color: chalk.red };
|
|
255
337
|
}
|
|
256
338
|
else if (pointsPossible) {
|
|
257
339
|
return { text: `–/${pointsPossible}`, color: chalk.gray };
|
|
258
340
|
}
|
|
259
|
-
return { text:
|
|
341
|
+
return { text: "N/A", color: chalk.gray };
|
|
260
342
|
}
|
|
261
343
|
export function formatDueDate(dueAt) {
|
|
262
344
|
if (!dueAt)
|
|
263
|
-
return
|
|
345
|
+
return "No due date";
|
|
264
346
|
return new Date(dueAt).toLocaleString();
|
|
265
347
|
}
|
|
266
348
|
export function displayAssignments(assignments, options = {}) {
|
|
267
349
|
const columns = [
|
|
268
|
-
{
|
|
350
|
+
{
|
|
351
|
+
key: "name",
|
|
352
|
+
header: "Assignment Name",
|
|
353
|
+
flex: 1,
|
|
354
|
+
minWidth: 15,
|
|
355
|
+
maxWidth: 35,
|
|
356
|
+
},
|
|
269
357
|
];
|
|
270
358
|
if (options.showId) {
|
|
271
|
-
columns.push({ key:
|
|
359
|
+
columns.push({ key: "id", header: "ID", width: 8 });
|
|
272
360
|
}
|
|
273
361
|
if (options.showGrade) {
|
|
274
362
|
columns.push({
|
|
275
|
-
key:
|
|
276
|
-
header:
|
|
277
|
-
|
|
278
|
-
maxWidth: 12,
|
|
363
|
+
key: "grade",
|
|
364
|
+
header: "Grade",
|
|
365
|
+
width: 10,
|
|
279
366
|
color: (value, row) => {
|
|
280
367
|
const gradeInfo = formatGrade(row._submission, row._pointsPossible);
|
|
281
368
|
return gradeInfo.color(value);
|
|
282
|
-
}
|
|
369
|
+
},
|
|
283
370
|
});
|
|
284
371
|
}
|
|
285
372
|
if (options.showDueDate) {
|
|
286
|
-
columns.push({
|
|
373
|
+
columns.push({
|
|
374
|
+
key: "dueDate",
|
|
375
|
+
header: "Due Date",
|
|
376
|
+
width: 16,
|
|
377
|
+
});
|
|
287
378
|
}
|
|
288
379
|
if (options.showStatus) {
|
|
289
380
|
columns.push({
|
|
290
|
-
key:
|
|
291
|
-
header:
|
|
292
|
-
|
|
293
|
-
maxWidth: 14,
|
|
381
|
+
key: "status",
|
|
382
|
+
header: "Status",
|
|
383
|
+
width: 12,
|
|
294
384
|
color: (value, row) => {
|
|
295
385
|
return row._isSubmitted ? chalk.green(value) : chalk.yellow(value);
|
|
296
|
-
}
|
|
386
|
+
},
|
|
297
387
|
});
|
|
298
388
|
}
|
|
299
389
|
const table = new Table(columns);
|
|
300
|
-
assignments.forEach(assignment => {
|
|
390
|
+
assignments.forEach((assignment) => {
|
|
301
391
|
const submission = assignment.submission;
|
|
302
392
|
const gradeInfo = formatGrade(submission, assignment.points_possible || 0);
|
|
303
393
|
const isSubmitted = !!(submission && submission.submitted_at);
|
|
@@ -306,75 +396,79 @@ export function displayAssignments(assignments, options = {}) {
|
|
|
306
396
|
id: assignment.id,
|
|
307
397
|
grade: gradeInfo.text,
|
|
308
398
|
dueDate: formatDueDate(assignment.due_at),
|
|
309
|
-
status: isSubmitted ?
|
|
399
|
+
status: isSubmitted ? "✓ Submitted" : "Not submit",
|
|
310
400
|
_submission: submission,
|
|
311
401
|
_pointsPossible: assignment.points_possible || 0,
|
|
312
|
-
_isSubmitted: isSubmitted
|
|
402
|
+
_isSubmitted: isSubmitted,
|
|
313
403
|
});
|
|
314
404
|
});
|
|
315
405
|
table.render();
|
|
316
406
|
}
|
|
317
407
|
function getAssignmentType(assignment) {
|
|
318
|
-
if (!assignment.submission_types ||
|
|
319
|
-
|
|
408
|
+
if (!assignment.submission_types ||
|
|
409
|
+
assignment.submission_types.length === 0) {
|
|
410
|
+
return "Unknown";
|
|
320
411
|
}
|
|
321
412
|
const types = assignment.submission_types;
|
|
322
|
-
if (types.includes(
|
|
323
|
-
return
|
|
413
|
+
if (types.includes("online_quiz")) {
|
|
414
|
+
return "Quiz";
|
|
324
415
|
}
|
|
325
|
-
else if (types.includes(
|
|
326
|
-
if (assignment.allowed_extensions &&
|
|
416
|
+
else if (types.includes("online_upload")) {
|
|
417
|
+
if (assignment.allowed_extensions &&
|
|
418
|
+
assignment.allowed_extensions.length > 0) {
|
|
327
419
|
const exts = assignment.allowed_extensions.slice(0, 3);
|
|
328
|
-
const extList = exts.join(
|
|
329
|
-
const suffix = assignment.allowed_extensions.length > 3 ?
|
|
420
|
+
const extList = exts.join(", ");
|
|
421
|
+
const suffix = assignment.allowed_extensions.length > 3 ? "..." : "";
|
|
330
422
|
return `${extList}${suffix}`;
|
|
331
423
|
}
|
|
332
|
-
return
|
|
424
|
+
return "Any file";
|
|
333
425
|
}
|
|
334
|
-
else if (types.includes(
|
|
335
|
-
return
|
|
426
|
+
else if (types.includes("online_text_entry")) {
|
|
427
|
+
return "Text Entry";
|
|
336
428
|
}
|
|
337
|
-
else if (types.includes(
|
|
338
|
-
return
|
|
429
|
+
else if (types.includes("online_url")) {
|
|
430
|
+
return "URL";
|
|
339
431
|
}
|
|
340
|
-
else if (types.includes(
|
|
341
|
-
return
|
|
432
|
+
else if (types.includes("external_tool")) {
|
|
433
|
+
return "External Tool";
|
|
342
434
|
}
|
|
343
|
-
else if (types.includes(
|
|
344
|
-
return
|
|
435
|
+
else if (types.includes("media_recording")) {
|
|
436
|
+
return "Media";
|
|
345
437
|
}
|
|
346
438
|
else if (types[0]) {
|
|
347
|
-
return types[0].replace(/_/g,
|
|
439
|
+
return types[0].replace(/_/g, " ");
|
|
348
440
|
}
|
|
349
|
-
return
|
|
441
|
+
return "Unknown";
|
|
350
442
|
}
|
|
351
443
|
export function displaySubmitAssignments(assignments) {
|
|
352
444
|
const columns = [
|
|
353
|
-
{ key:
|
|
354
|
-
{ key:
|
|
355
|
-
{ key:
|
|
445
|
+
{ key: "name", header: "Assignment Name", flex: 1, minWidth: 15 },
|
|
446
|
+
{ key: "type", header: "Type", width: 8 },
|
|
447
|
+
{ key: "dueDate", header: "Due", width: 10 },
|
|
356
448
|
{
|
|
357
|
-
key:
|
|
358
|
-
header:
|
|
359
|
-
|
|
360
|
-
maxWidth: 14,
|
|
449
|
+
key: "status",
|
|
450
|
+
header: "Status",
|
|
451
|
+
width: 10,
|
|
361
452
|
color: (value, row) => {
|
|
362
453
|
return row._isSubmitted ? chalk.green(value) : chalk.yellow(value);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
454
|
+
},
|
|
455
|
+
},
|
|
365
456
|
];
|
|
366
457
|
const table = new Table(columns);
|
|
367
|
-
assignments.forEach(assignment => {
|
|
458
|
+
assignments.forEach((assignment) => {
|
|
368
459
|
const submission = assignment.submission;
|
|
369
460
|
const isSubmitted = !!(submission && submission.submitted_at);
|
|
461
|
+
let dueDate = "No due date";
|
|
462
|
+
if (assignment.due_at) {
|
|
463
|
+
const d = new Date(assignment.due_at);
|
|
464
|
+
dueDate = `${(d.getMonth() + 1).toString().padStart(2, "0")}/${d.getDate().toString().padStart(2, "0")}/${d.getFullYear()}`;
|
|
465
|
+
}
|
|
370
466
|
table.addRow({
|
|
371
467
|
name: assignment.name,
|
|
372
468
|
type: getAssignmentType(assignment),
|
|
373
|
-
dueDate:
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
status: isSubmitted ? '✓ Submitted' : 'Not submitted',
|
|
377
|
-
_isSubmitted: isSubmitted
|
|
469
|
+
dueDate: dueDate,
|
|
470
|
+
status: isSubmitted ? "✓ Submitted" : "Not submit",
|
|
471
|
+
_isSubmitted: isSubmitted,
|
|
378
472
|
});
|
|
379
473
|
});
|
|
380
474
|
table.renderWithResize();
|
|
@@ -382,30 +476,30 @@ export function displaySubmitAssignments(assignments) {
|
|
|
382
476
|
}
|
|
383
477
|
export function displayAnnouncements(announcements) {
|
|
384
478
|
const columns = [
|
|
385
|
-
{ key:
|
|
386
|
-
{ key:
|
|
479
|
+
{ key: "title", header: "Title", flex: 1, minWidth: 15 },
|
|
480
|
+
{ key: "posted", header: "Posted", width: 10 },
|
|
387
481
|
];
|
|
388
482
|
const table = new Table(columns);
|
|
389
|
-
announcements.forEach(announcement => {
|
|
483
|
+
announcements.forEach((announcement) => {
|
|
390
484
|
const date = announcement.posted_at
|
|
391
485
|
? new Date(announcement.posted_at).toLocaleDateString()
|
|
392
|
-
:
|
|
486
|
+
: "N/A";
|
|
393
487
|
table.addRow({
|
|
394
|
-
title: announcement.title ||
|
|
395
|
-
posted: date
|
|
488
|
+
title: announcement.title || "Untitled",
|
|
489
|
+
posted: date,
|
|
396
490
|
});
|
|
397
491
|
});
|
|
398
492
|
table.renderWithResize();
|
|
399
493
|
return table;
|
|
400
494
|
}
|
|
401
|
-
export function printSeparator(char =
|
|
402
|
-
const width = length ||
|
|
495
|
+
export function printSeparator(char = "─", length) {
|
|
496
|
+
const width = length || process.stdout.columns || 60;
|
|
403
497
|
console.log(chalk.cyan.bold(char.repeat(width)));
|
|
404
498
|
}
|
|
405
499
|
export function printHeader(title) {
|
|
406
|
-
console.log(chalk.cyan.bold(
|
|
500
|
+
console.log(chalk.cyan.bold("\n" + "─".repeat(60)));
|
|
407
501
|
console.log(chalk.cyan.bold(title));
|
|
408
|
-
console.log(chalk.cyan.bold(
|
|
502
|
+
console.log(chalk.cyan.bold("─".repeat(60)));
|
|
409
503
|
}
|
|
410
504
|
export function printSuccess(message) {
|
|
411
505
|
console.log(chalk.green(`✓ ${message}`));
|
|
@@ -420,40 +514,40 @@ export function printWarning(message) {
|
|
|
420
514
|
console.log(chalk.yellow(message));
|
|
421
515
|
}
|
|
422
516
|
function cleanHtmlContent(html) {
|
|
423
|
-
return html
|
|
424
|
-
.replace(/ /g,
|
|
425
|
-
.replace(/&/g,
|
|
426
|
-
.replace(/</g,
|
|
427
|
-
.replace(/>/g,
|
|
517
|
+
return (html
|
|
518
|
+
.replace(/ /g, " ")
|
|
519
|
+
.replace(/&/g, "&")
|
|
520
|
+
.replace(/</g, "<")
|
|
521
|
+
.replace(/>/g, ">")
|
|
428
522
|
.replace(/"/g, '"')
|
|
429
523
|
.replace(/'/g, "'")
|
|
430
524
|
.replace(/'/g, "'")
|
|
431
|
-
.replace(/<br\s*\/?>/gi,
|
|
432
|
-
.replace(/<\/(p|div|li|h[1-6])>/gi,
|
|
433
|
-
.replace(/<li[^>]*>/gi,
|
|
434
|
-
.replace(/<[^>]+>/g,
|
|
435
|
-
.replace(/\n{3,}/g,
|
|
436
|
-
.replace(/[ \t]+/g,
|
|
437
|
-
.split(
|
|
438
|
-
.map(line => line.trim())
|
|
439
|
-
.join(
|
|
440
|
-
.trim();
|
|
525
|
+
.replace(/<br\s*\/?>/gi, "\n")
|
|
526
|
+
.replace(/<\/(p|div|li|h[1-6])>/gi, "\n")
|
|
527
|
+
.replace(/<li[^>]*>/gi, "• ")
|
|
528
|
+
.replace(/<[^>]+>/g, "")
|
|
529
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
530
|
+
.replace(/[ \t]+/g, " ")
|
|
531
|
+
.split("\n")
|
|
532
|
+
.map((line) => line.trim())
|
|
533
|
+
.join("\n")
|
|
534
|
+
.trim());
|
|
441
535
|
}
|
|
442
536
|
function wordWrap(text, maxWidth) {
|
|
443
537
|
const lines = [];
|
|
444
|
-
const paragraphs = text.split(
|
|
538
|
+
const paragraphs = text.split("\n");
|
|
445
539
|
for (const paragraph of paragraphs) {
|
|
446
|
-
if (paragraph.trim() ===
|
|
447
|
-
lines.push(
|
|
540
|
+
if (paragraph.trim() === "") {
|
|
541
|
+
lines.push("");
|
|
448
542
|
continue;
|
|
449
543
|
}
|
|
450
|
-
const words = paragraph.split(
|
|
451
|
-
let currentLine =
|
|
544
|
+
const words = paragraph.split(" ").filter((w) => w.length > 0);
|
|
545
|
+
let currentLine = "";
|
|
452
546
|
for (const word of words) {
|
|
453
547
|
if (word.length > maxWidth) {
|
|
454
548
|
if (currentLine) {
|
|
455
549
|
lines.push(currentLine);
|
|
456
|
-
currentLine =
|
|
550
|
+
currentLine = "";
|
|
457
551
|
}
|
|
458
552
|
for (let i = 0; i < word.length; i += maxWidth) {
|
|
459
553
|
lines.push(word.substring(i, i + maxWidth));
|
|
@@ -481,27 +575,29 @@ export function displayAnnouncementDetail(announcement) {
|
|
|
481
575
|
const terminalWidth = process.stdout.columns || 80;
|
|
482
576
|
const boxWidth = Math.max(50, terminalWidth - 4);
|
|
483
577
|
const contentWidth = boxWidth - 4;
|
|
484
|
-
const title = announcement.title ||
|
|
485
|
-
const date = announcement.postedAt
|
|
486
|
-
|
|
487
|
-
|
|
578
|
+
const title = announcement.title || "Untitled";
|
|
579
|
+
const date = announcement.postedAt
|
|
580
|
+
? new Date(announcement.postedAt).toLocaleString()
|
|
581
|
+
: "N/A";
|
|
582
|
+
const author = announcement.author || "Unknown";
|
|
583
|
+
const cleanedMessage = cleanHtmlContent(announcement.message || "No content");
|
|
488
584
|
const titleLines = wordWrap(title, contentWidth);
|
|
489
585
|
const messageLines = wordWrap(cleanedMessage, contentWidth);
|
|
490
586
|
const printLine = (content, style = chalk.white) => {
|
|
491
587
|
const paddedContent = content.padEnd(contentWidth);
|
|
492
|
-
console.log(chalk.gray(
|
|
588
|
+
console.log(chalk.gray("│ ") + style(paddedContent) + chalk.gray(" │"));
|
|
493
589
|
};
|
|
494
|
-
console.log(
|
|
590
|
+
console.log("\n" + chalk.gray("╭" + "─".repeat(boxWidth - 2) + "╮"));
|
|
495
591
|
for (const line of titleLines) {
|
|
496
592
|
printLine(line, chalk.bold.white);
|
|
497
593
|
}
|
|
498
|
-
console.log(chalk.gray(
|
|
594
|
+
console.log(chalk.gray("├" + "─".repeat(boxWidth - 2) + "┤"));
|
|
499
595
|
printLine(`Posted: ${date}`, chalk.gray);
|
|
500
596
|
printLine(`Author: ${author}`, chalk.gray);
|
|
501
|
-
console.log(chalk.gray(
|
|
597
|
+
console.log(chalk.gray("├" + "─".repeat(boxWidth - 2) + "┤"));
|
|
502
598
|
for (const line of messageLines) {
|
|
503
599
|
printLine(line, chalk.white);
|
|
504
600
|
}
|
|
505
|
-
console.log(chalk.gray(
|
|
601
|
+
console.log(chalk.gray("╰" + "─".repeat(boxWidth - 2) + "╯") + "\n");
|
|
506
602
|
}
|
|
507
603
|
//# sourceMappingURL=display.js.map
|