canvaslms-cli 1.6.0 → 1.6.3
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 +210 -186
- 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 +43 -124
- 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 +40 -128
- package/dist/commands/assignments.js.map +1 -1
- 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 -178
- 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 +26 -46
- package/dist/commands/list.js.map +1 -1
- 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 +2 -2
- package/dist/commands/submit.d.ts.map +1 -1
- package/dist/commands/submit.js +110 -165
- package/dist/commands/submit.js.map +1 -1
- package/dist/index.d.ts +14 -14
- package/dist/index.js +14 -14
- 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 +84 -0
- package/dist/lib/display.d.ts.map +1 -0
- package/dist/lib/display.js +603 -0
- package/dist/lib/display.js.map +1 -0
- 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 +200 -143
- package/dist/lib/interactive.js.map +1 -1
- package/dist/src/index.d.ts +0 -1
- package/dist/src/index.js +62 -62
- package/dist/src/index.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/package.json +9 -4
- 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/commands/grades.js
CHANGED
|
@@ -1,32 +1,30 @@
|
|
|
1
|
-
import { makeCanvasRequest } from
|
|
2
|
-
import { createReadlineInterface, askQuestionWithValidation } from
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
return str + ' '.repeat(Math.max(0, len - str.length));
|
|
6
|
-
}
|
|
1
|
+
import { makeCanvasRequest, getCanvasCourse } from "../lib/api-client.js";
|
|
2
|
+
import { createReadlineInterface, askQuestionWithValidation, } from "../lib/interactive.js";
|
|
3
|
+
import { pad } from "../lib/display.js";
|
|
4
|
+
import chalk from "chalk";
|
|
7
5
|
async function showDetailedGrades(courseId, options) {
|
|
8
|
-
console.log(chalk.cyan.bold(
|
|
9
|
-
console.log(chalk.cyan.bold(
|
|
10
|
-
const course = await makeCanvasRequest(
|
|
11
|
-
const enrollmentParams = [
|
|
12
|
-
enrollmentParams.push(
|
|
13
|
-
enrollmentParams.push(
|
|
14
|
-
enrollmentParams.push(
|
|
15
|
-
enrollmentParams.push(
|
|
16
|
-
enrollmentParams.push(
|
|
17
|
-
enrollmentParams.push(
|
|
18
|
-
const enrollments = await makeCanvasRequest(
|
|
6
|
+
console.log(chalk.cyan.bold("\n" + "=".repeat(80)));
|
|
7
|
+
console.log(chalk.cyan.bold("Loading course grades, please wait..."));
|
|
8
|
+
const course = await makeCanvasRequest("get", `courses/${courseId}`);
|
|
9
|
+
const enrollmentParams = ["user_id=self", "include[]=total_scores"];
|
|
10
|
+
enrollmentParams.push("state[]=active");
|
|
11
|
+
enrollmentParams.push("state[]=invited");
|
|
12
|
+
enrollmentParams.push("state[]=creation_pending");
|
|
13
|
+
enrollmentParams.push("state[]=rejected");
|
|
14
|
+
enrollmentParams.push("state[]=completed");
|
|
15
|
+
enrollmentParams.push("state[]=inactive");
|
|
16
|
+
const enrollments = await makeCanvasRequest("get", `courses/${courseId}/enrollments`, enrollmentParams);
|
|
19
17
|
if (!enrollments || enrollments.length === 0) {
|
|
20
|
-
console.log(chalk.red(
|
|
18
|
+
console.log(chalk.red("Error: No enrollment found for this course."));
|
|
21
19
|
return;
|
|
22
20
|
}
|
|
23
21
|
const enrollment = enrollments[0];
|
|
24
22
|
const grades = enrollment?.grades;
|
|
25
|
-
const assignments = await makeCanvasRequest(
|
|
26
|
-
console.log(chalk.cyan.bold(
|
|
23
|
+
const assignments = await makeCanvasRequest("get", `courses/${courseId}/assignments`, ["include[]=submission", "per_page=100"]);
|
|
24
|
+
console.log(chalk.cyan.bold("\n" + "=".repeat(80)));
|
|
27
25
|
console.log(chalk.cyan.bold(`Course: ${course.name}`));
|
|
28
|
-
console.log(chalk.cyan(
|
|
29
|
-
const assignmentGrades = assignments.map(assignment => {
|
|
26
|
+
console.log(chalk.cyan("=".repeat(80)));
|
|
27
|
+
const assignmentGrades = assignments.map((assignment) => {
|
|
30
28
|
const submission = assignment.submission;
|
|
31
29
|
return {
|
|
32
30
|
name: assignment.name,
|
|
@@ -35,67 +33,117 @@ async function showDetailedGrades(courseId, options) {
|
|
|
35
33
|
pointsPossible: assignment.points_possible || 0,
|
|
36
34
|
submitted: !!(submission && submission.submitted_at),
|
|
37
35
|
graded: submission?.score !== null && submission?.score !== undefined,
|
|
38
|
-
dueAt: assignment.due_at
|
|
36
|
+
dueAt: assignment.due_at,
|
|
39
37
|
};
|
|
40
38
|
});
|
|
41
|
-
const gradedAssignments = assignmentGrades.filter(a => a.graded);
|
|
39
|
+
const gradedAssignments = assignmentGrades.filter((a) => a.graded);
|
|
42
40
|
const totalPointsEarned = gradedAssignments.reduce((sum, a) => sum + (a.score || 0), 0);
|
|
43
41
|
const totalPointsPossible = gradedAssignments.reduce((sum, a) => sum + a.pointsPossible, 0);
|
|
44
42
|
const calculatedPercentage = totalPointsPossible > 0
|
|
45
43
|
? ((totalPointsEarned / totalPointsPossible) * 100).toFixed(2)
|
|
46
|
-
:
|
|
47
|
-
console.log(chalk.white.bold(
|
|
44
|
+
: "N/A";
|
|
45
|
+
console.log(chalk.white.bold("\nOverall Grades:"));
|
|
48
46
|
const termWidth = process.stdout.columns || 80;
|
|
49
47
|
const borderOverhead = 10;
|
|
50
48
|
const availableWidth = Math.max(60, termWidth - borderOverhead);
|
|
51
49
|
const colMetric = Math.max(18, Math.floor(availableWidth * 0.35));
|
|
52
|
-
const colScore = Math.max(20, Math.floor(availableWidth * 0.
|
|
50
|
+
const colScore = Math.max(20, Math.floor(availableWidth * 0.4));
|
|
53
51
|
const colStatus = Math.max(12, availableWidth - colMetric - colScore);
|
|
54
|
-
console.log(chalk.gray(
|
|
55
|
-
chalk.gray(
|
|
56
|
-
chalk.gray(
|
|
57
|
-
|
|
58
|
-
chalk.
|
|
59
|
-
chalk.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
chalk.
|
|
52
|
+
console.log(chalk.gray("╭─") +
|
|
53
|
+
chalk.gray("─".repeat(colMetric)) +
|
|
54
|
+
chalk.gray("┬─") +
|
|
55
|
+
chalk.gray("─".repeat(colScore)) +
|
|
56
|
+
chalk.gray("┬─") +
|
|
57
|
+
chalk.gray("─".repeat(colStatus)) +
|
|
58
|
+
chalk.gray("╮"));
|
|
59
|
+
console.log(chalk.gray("│ ") +
|
|
60
|
+
chalk.cyan.bold(pad("Metric", colMetric)) +
|
|
61
|
+
chalk.gray("│ ") +
|
|
62
|
+
chalk.cyan.bold(pad("Score/Grade", colScore)) +
|
|
63
|
+
chalk.gray("│ ") +
|
|
64
|
+
chalk.cyan.bold(pad("Status", colStatus)) +
|
|
65
|
+
chalk.gray("│"));
|
|
66
|
+
console.log(chalk.gray("├─") +
|
|
67
|
+
chalk.gray("─".repeat(colMetric)) +
|
|
68
|
+
chalk.gray("┼─") +
|
|
69
|
+
chalk.gray("─".repeat(colScore)) +
|
|
70
|
+
chalk.gray("┼─") +
|
|
71
|
+
chalk.gray("─".repeat(colStatus)) +
|
|
72
|
+
chalk.gray("┤"));
|
|
63
73
|
if (grades) {
|
|
64
|
-
const currentScoreValue = grades.current_score !== null ? `${grades.current_score}%` :
|
|
65
|
-
const finalScoreValue = grades.final_score !== null ? `${grades.final_score}%` :
|
|
66
|
-
const currentGradeValue = grades.current_grade ||
|
|
67
|
-
const finalGradeValue = grades.final_grade ||
|
|
68
|
-
console.log(chalk.gray(
|
|
69
|
-
chalk.
|
|
70
|
-
chalk.gray(
|
|
71
|
-
|
|
72
|
-
chalk.
|
|
73
|
-
chalk.gray(pad(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
chalk.
|
|
77
|
-
|
|
78
|
-
chalk.green.bold(pad(
|
|
79
|
-
chalk.gray(
|
|
80
|
-
|
|
81
|
-
chalk.gray(
|
|
82
|
-
|
|
74
|
+
const currentScoreValue = grades.current_score !== null ? `${grades.current_score}%` : "N/A";
|
|
75
|
+
const finalScoreValue = grades.final_score !== null ? `${grades.final_score}%` : "N/A";
|
|
76
|
+
const currentGradeValue = grades.current_grade || "N/A";
|
|
77
|
+
const finalGradeValue = grades.final_grade || "N/A";
|
|
78
|
+
console.log(chalk.gray("│ ") +
|
|
79
|
+
chalk.white(pad("Current Score", colMetric)) +
|
|
80
|
+
chalk.gray("│ ") +
|
|
81
|
+
chalk.green.bold(pad(currentScoreValue, colScore)) +
|
|
82
|
+
chalk.gray("│ ") +
|
|
83
|
+
chalk.gray(pad("Official", colStatus)) +
|
|
84
|
+
chalk.gray("│"));
|
|
85
|
+
console.log(chalk.gray("│ ") +
|
|
86
|
+
chalk.white(pad("Final Score", colMetric)) +
|
|
87
|
+
chalk.gray("│ ") +
|
|
88
|
+
chalk.green.bold(pad(finalScoreValue, colScore)) +
|
|
89
|
+
chalk.gray("│ ") +
|
|
90
|
+
chalk.gray(pad("Official", colStatus)) +
|
|
91
|
+
chalk.gray("│"));
|
|
92
|
+
console.log(chalk.gray("│ ") +
|
|
93
|
+
chalk.white(pad("Current Grade", colMetric)) +
|
|
94
|
+
chalk.gray("│ ") +
|
|
95
|
+
chalk.green.bold(pad(currentGradeValue, colScore)) +
|
|
96
|
+
chalk.gray("│ ") +
|
|
97
|
+
chalk.gray(pad("Letter Grade", colStatus)) +
|
|
98
|
+
chalk.gray("│"));
|
|
99
|
+
console.log(chalk.gray("│ ") +
|
|
100
|
+
chalk.white(pad("Final Grade", colMetric)) +
|
|
101
|
+
chalk.gray("│ ") +
|
|
102
|
+
chalk.green.bold(pad(finalGradeValue, colScore)) +
|
|
103
|
+
chalk.gray("│ ") +
|
|
104
|
+
chalk.gray(pad("Letter Grade", colStatus)) +
|
|
105
|
+
chalk.gray("│"));
|
|
106
|
+
console.log(chalk.gray("├─") +
|
|
107
|
+
chalk.gray("─".repeat(colMetric)) +
|
|
108
|
+
chalk.gray("┼─") +
|
|
109
|
+
chalk.gray("─".repeat(colScore)) +
|
|
110
|
+
chalk.gray("┼─") +
|
|
111
|
+
chalk.gray("─".repeat(colStatus)) +
|
|
112
|
+
chalk.gray("┤"));
|
|
83
113
|
}
|
|
84
|
-
console.log(chalk.gray(
|
|
85
|
-
chalk.
|
|
86
|
-
chalk.gray(
|
|
87
|
-
|
|
88
|
-
chalk.
|
|
89
|
-
chalk.gray(pad(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
chalk.
|
|
93
|
-
|
|
94
|
-
chalk.
|
|
95
|
-
chalk.gray(
|
|
96
|
-
|
|
114
|
+
console.log(chalk.gray("│ ") +
|
|
115
|
+
chalk.white(pad("Graded Assignments", colMetric)) +
|
|
116
|
+
chalk.gray("│ ") +
|
|
117
|
+
chalk.cyan.bold(pad(`${gradedAssignments.length} / ${assignments.length}`, colScore)) +
|
|
118
|
+
chalk.gray("│ ") +
|
|
119
|
+
chalk.gray(pad("Completed", colStatus)) +
|
|
120
|
+
chalk.gray("│"));
|
|
121
|
+
console.log(chalk.gray("│ ") +
|
|
122
|
+
chalk.white(pad("Points Earned", colMetric)) +
|
|
123
|
+
chalk.gray("│ ") +
|
|
124
|
+
chalk.cyan.bold(pad(`${totalPointsEarned.toFixed(2)} / ${totalPointsPossible.toFixed(2)}`, colScore)) +
|
|
125
|
+
chalk.gray("│ ") +
|
|
126
|
+
chalk.gray(pad("Total", colStatus)) +
|
|
127
|
+
chalk.gray("│"));
|
|
128
|
+
console.log(chalk.gray("│ ") +
|
|
129
|
+
chalk.white(pad("Calculated Average", colMetric)) +
|
|
130
|
+
chalk.gray("│ ") +
|
|
131
|
+
chalk.cyan.bold(pad(typeof calculatedPercentage === "string"
|
|
132
|
+
? calculatedPercentage
|
|
133
|
+
: `${calculatedPercentage}%`, colScore)) +
|
|
134
|
+
chalk.gray("│ ") +
|
|
135
|
+
chalk.gray(pad("From Graded", colStatus)) +
|
|
136
|
+
chalk.gray("│"));
|
|
137
|
+
console.log(chalk.gray("╰─") +
|
|
138
|
+
chalk.gray("─".repeat(colMetric)) +
|
|
139
|
+
chalk.gray("┴─") +
|
|
140
|
+
chalk.gray("─".repeat(colScore)) +
|
|
141
|
+
chalk.gray("┴─") +
|
|
142
|
+
chalk.gray("─".repeat(colStatus)) +
|
|
143
|
+
chalk.gray("╯"));
|
|
144
|
+
console.log(chalk.white.bold("\nAssignment Breakdown:"));
|
|
97
145
|
if (assignments.length === 0) {
|
|
98
|
-
console.log(chalk.yellow(
|
|
146
|
+
console.log(chalk.yellow(" No assignments found for this course."));
|
|
99
147
|
}
|
|
100
148
|
else {
|
|
101
149
|
const tw = process.stdout.columns || 100;
|
|
@@ -106,200 +154,270 @@ async function showDetailedGrades(courseId, options) {
|
|
|
106
154
|
const colStatus = Math.max(10, Math.min(15, Math.floor(available * 0.12)));
|
|
107
155
|
const colDate = Math.max(12, Math.min(20, Math.floor(available * 0.18)));
|
|
108
156
|
const colName = Math.max(20, available - colNo - colScore - colStatus - colDate);
|
|
109
|
-
console.log(chalk.gray(
|
|
110
|
-
chalk.gray(
|
|
111
|
-
chalk.gray(
|
|
112
|
-
chalk.gray(
|
|
113
|
-
chalk.gray(
|
|
114
|
-
|
|
115
|
-
chalk.
|
|
116
|
-
chalk.
|
|
117
|
-
chalk.
|
|
118
|
-
chalk.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
chalk.
|
|
122
|
-
chalk.gray(
|
|
123
|
-
chalk.
|
|
157
|
+
console.log(chalk.gray("╭─") +
|
|
158
|
+
chalk.gray("─".repeat(colNo)) +
|
|
159
|
+
chalk.gray("┬─") +
|
|
160
|
+
chalk.gray("─".repeat(colName)) +
|
|
161
|
+
chalk.gray("┬─") +
|
|
162
|
+
chalk.gray("─".repeat(colScore)) +
|
|
163
|
+
chalk.gray("┬─") +
|
|
164
|
+
chalk.gray("─".repeat(colStatus)) +
|
|
165
|
+
chalk.gray("┬─") +
|
|
166
|
+
chalk.gray("─".repeat(colDate)) +
|
|
167
|
+
chalk.gray("╮"));
|
|
168
|
+
console.log(chalk.gray("│ ") +
|
|
169
|
+
chalk.cyan.bold(pad("#", colNo)) +
|
|
170
|
+
chalk.gray("│ ") +
|
|
171
|
+
chalk.cyan.bold(pad("Assignment Name", colName)) +
|
|
172
|
+
chalk.gray("│ ") +
|
|
173
|
+
chalk.cyan.bold(pad("Score", colScore)) +
|
|
174
|
+
chalk.gray("│ ") +
|
|
175
|
+
chalk.cyan.bold(pad("Status", colStatus)) +
|
|
176
|
+
chalk.gray("│ ") +
|
|
177
|
+
chalk.cyan.bold(pad("Due Date", colDate)) +
|
|
178
|
+
chalk.gray("│"));
|
|
179
|
+
console.log(chalk.gray("├─") +
|
|
180
|
+
chalk.gray("─".repeat(colNo)) +
|
|
181
|
+
chalk.gray("┼─") +
|
|
182
|
+
chalk.gray("─".repeat(colName)) +
|
|
183
|
+
chalk.gray("┼─") +
|
|
184
|
+
chalk.gray("─".repeat(colScore)) +
|
|
185
|
+
chalk.gray("┼─") +
|
|
186
|
+
chalk.gray("─".repeat(colStatus)) +
|
|
187
|
+
chalk.gray("┼─") +
|
|
188
|
+
chalk.gray("─".repeat(colDate)) +
|
|
189
|
+
chalk.gray("┤"));
|
|
124
190
|
assignmentGrades.forEach((assignment, index) => {
|
|
125
191
|
const scoreDisplay = assignment.graded
|
|
126
192
|
? `${(assignment.score || 0).toFixed(1)}/${assignment.pointsPossible}`
|
|
127
193
|
: assignment.pointsPossible > 0
|
|
128
194
|
? `–/${assignment.pointsPossible}`
|
|
129
|
-
:
|
|
130
|
-
let statusDisplay =
|
|
195
|
+
: "N/A";
|
|
196
|
+
let statusDisplay = "";
|
|
131
197
|
let statusColor = chalk.gray;
|
|
132
198
|
if (assignment.graded) {
|
|
133
199
|
const percentage = assignment.pointsPossible > 0
|
|
134
200
|
? ((assignment.score || 0) / assignment.pointsPossible) * 100
|
|
135
201
|
: 0;
|
|
136
202
|
if (percentage >= 80) {
|
|
137
|
-
statusDisplay =
|
|
203
|
+
statusDisplay = "✓ Graded";
|
|
138
204
|
statusColor = chalk.green;
|
|
139
205
|
}
|
|
140
206
|
else if (percentage >= 50) {
|
|
141
|
-
statusDisplay =
|
|
207
|
+
statusDisplay = "✓ Graded";
|
|
142
208
|
statusColor = chalk.yellow;
|
|
143
209
|
}
|
|
144
210
|
else {
|
|
145
|
-
statusDisplay =
|
|
211
|
+
statusDisplay = "✓ Graded";
|
|
146
212
|
statusColor = chalk.red;
|
|
147
213
|
}
|
|
148
214
|
}
|
|
149
215
|
else if (assignment.submitted) {
|
|
150
|
-
statusDisplay =
|
|
216
|
+
statusDisplay = "Pending";
|
|
151
217
|
statusColor = chalk.cyan;
|
|
152
218
|
}
|
|
153
219
|
else {
|
|
154
|
-
statusDisplay =
|
|
220
|
+
statusDisplay = "Not Done";
|
|
155
221
|
statusColor = chalk.gray;
|
|
156
222
|
}
|
|
157
223
|
const dueDate = assignment.dueAt
|
|
158
224
|
? new Date(assignment.dueAt).toLocaleDateString()
|
|
159
|
-
:
|
|
225
|
+
: "No due date";
|
|
160
226
|
let displayName = assignment.name;
|
|
161
227
|
if (displayName.length > colName) {
|
|
162
|
-
displayName = displayName.substring(0, colName - 3) +
|
|
228
|
+
displayName = displayName.substring(0, colName - 3) + "...";
|
|
163
229
|
}
|
|
164
|
-
console.log(chalk.gray(
|
|
165
|
-
chalk.white(pad(
|
|
166
|
-
chalk.
|
|
167
|
-
|
|
168
|
-
chalk.gray(
|
|
230
|
+
console.log(chalk.gray("│ ") +
|
|
231
|
+
chalk.white(pad((index + 1).toString(), colNo)) +
|
|
232
|
+
chalk.gray("│ ") +
|
|
233
|
+
chalk.white(pad(displayName, colName)) +
|
|
234
|
+
chalk.gray("│ ") +
|
|
235
|
+
chalk.white(pad(scoreDisplay, colScore)) +
|
|
236
|
+
chalk.gray("│ ") +
|
|
237
|
+
statusColor(pad(statusDisplay, colStatus)) +
|
|
238
|
+
chalk.gray("│ ") +
|
|
239
|
+
chalk.gray(pad(dueDate, colDate)) +
|
|
240
|
+
chalk.gray("│"));
|
|
169
241
|
});
|
|
170
|
-
console.log(chalk.gray(
|
|
171
|
-
chalk.gray(
|
|
172
|
-
chalk.gray(
|
|
173
|
-
chalk.gray(
|
|
174
|
-
chalk.gray(
|
|
242
|
+
console.log(chalk.gray("╰─") +
|
|
243
|
+
chalk.gray("─".repeat(colNo)) +
|
|
244
|
+
chalk.gray("┴─") +
|
|
245
|
+
chalk.gray("─".repeat(colName)) +
|
|
246
|
+
chalk.gray("┴─") +
|
|
247
|
+
chalk.gray("─".repeat(colScore)) +
|
|
248
|
+
chalk.gray("┴─") +
|
|
249
|
+
chalk.gray("─".repeat(colStatus)) +
|
|
250
|
+
chalk.gray("┴─") +
|
|
251
|
+
chalk.gray("─".repeat(colDate)) +
|
|
252
|
+
chalk.gray("╯"));
|
|
175
253
|
}
|
|
176
|
-
console.log(chalk.cyan(
|
|
254
|
+
console.log(chalk.cyan("=".repeat(80)));
|
|
177
255
|
if (options.verbose && enrollment) {
|
|
178
|
-
console.log(chalk.cyan(
|
|
179
|
-
console.log(chalk.cyan.bold(
|
|
180
|
-
console.log(chalk.white(
|
|
181
|
-
console.log(chalk.white(
|
|
182
|
-
console.log(chalk.white(
|
|
183
|
-
console.log(chalk.cyan(
|
|
256
|
+
console.log(chalk.cyan("\n" + "-".repeat(80)));
|
|
257
|
+
console.log(chalk.cyan.bold("Enrollment Details:"));
|
|
258
|
+
console.log(chalk.white(" Enrollment ID: ") + enrollment.id);
|
|
259
|
+
console.log(chalk.white(" Type: ") + enrollment.type);
|
|
260
|
+
console.log(chalk.white(" State: ") + enrollment.enrollment_state);
|
|
261
|
+
console.log(chalk.cyan("-".repeat(80)));
|
|
184
262
|
}
|
|
185
263
|
}
|
|
186
|
-
export async function showGrades(
|
|
264
|
+
export async function showGrades(courseName, options = {}) {
|
|
187
265
|
try {
|
|
188
|
-
if (
|
|
189
|
-
await
|
|
266
|
+
if (courseName) {
|
|
267
|
+
const course = await getCanvasCourse(courseName);
|
|
268
|
+
if (!course) {
|
|
269
|
+
console.log(chalk.red(`Error: Course "${courseName}" not found.`));
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
console.log(chalk.green(`✓ Using course: ${course.name}`));
|
|
273
|
+
await showDetailedGrades(course.id.toString(), options);
|
|
190
274
|
}
|
|
191
275
|
else {
|
|
192
276
|
const rl = createReadlineInterface();
|
|
193
|
-
console.log(chalk.cyan.bold(
|
|
194
|
-
console.log(chalk.cyan.bold(
|
|
195
|
-
const enrollmentState = options.all ? undefined :
|
|
277
|
+
console.log(chalk.cyan.bold("\n" + "=".repeat(80)));
|
|
278
|
+
console.log(chalk.cyan.bold("Loading your courses, please wait..."));
|
|
279
|
+
const enrollmentState = options.all ? undefined : "active";
|
|
196
280
|
const queryParams = [
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
281
|
+
"include[]=total_scores",
|
|
282
|
+
"include[]=current_grading_period_scores",
|
|
283
|
+
"per_page=100",
|
|
200
284
|
];
|
|
201
285
|
if (enrollmentState) {
|
|
202
286
|
queryParams.unshift(`enrollment_state=${enrollmentState}`);
|
|
203
287
|
}
|
|
204
|
-
let courses = await makeCanvasRequest(
|
|
288
|
+
let courses = await makeCanvasRequest("get", "courses", queryParams);
|
|
205
289
|
if (!courses || courses.length === 0) {
|
|
206
|
-
console.log(chalk.red(
|
|
290
|
+
console.log(chalk.red("Error: No courses found."));
|
|
207
291
|
rl.close();
|
|
208
292
|
return;
|
|
209
293
|
}
|
|
210
294
|
if (courses.length === 0) {
|
|
211
|
-
console.log(chalk.red(
|
|
295
|
+
console.log(chalk.red("Error: No courses found (after filtering)."));
|
|
212
296
|
rl.close();
|
|
213
297
|
return;
|
|
214
298
|
}
|
|
215
299
|
const coursesWithGrades = [];
|
|
216
300
|
for (const course of courses) {
|
|
217
301
|
try {
|
|
218
|
-
const enrollmentParams = [
|
|
219
|
-
enrollmentParams.push(
|
|
220
|
-
enrollmentParams.push(
|
|
221
|
-
enrollmentParams.push(
|
|
222
|
-
enrollmentParams.push(
|
|
223
|
-
enrollmentParams.push(
|
|
224
|
-
enrollmentParams.push(
|
|
225
|
-
const enrollments = await makeCanvasRequest(
|
|
302
|
+
const enrollmentParams = ["user_id=self", "include[]=total_scores"];
|
|
303
|
+
enrollmentParams.push("state[]=active");
|
|
304
|
+
enrollmentParams.push("state[]=invited");
|
|
305
|
+
enrollmentParams.push("state[]=creation_pending");
|
|
306
|
+
enrollmentParams.push("state[]=rejected");
|
|
307
|
+
enrollmentParams.push("state[]=completed");
|
|
308
|
+
enrollmentParams.push("state[]=inactive");
|
|
309
|
+
const enrollments = await makeCanvasRequest("get", `courses/${course.id}/enrollments`, enrollmentParams);
|
|
226
310
|
coursesWithGrades.push({
|
|
227
311
|
course,
|
|
228
|
-
enrollment: enrollments[0] || null
|
|
312
|
+
enrollment: enrollments[0] || null,
|
|
229
313
|
});
|
|
230
314
|
}
|
|
231
|
-
catch
|
|
315
|
+
catch {
|
|
232
316
|
coursesWithGrades.push({
|
|
233
317
|
course,
|
|
234
|
-
enrollment: null
|
|
318
|
+
enrollment: null,
|
|
235
319
|
});
|
|
236
320
|
}
|
|
237
321
|
}
|
|
238
|
-
console.log(chalk.cyan.bold(
|
|
239
|
-
console.log(chalk.cyan.bold(
|
|
240
|
-
console.log(chalk.green(`✓ Found ${coursesWithGrades.length} course(s)${options.all ?
|
|
322
|
+
console.log(chalk.cyan.bold("\n" + "=".repeat(80)));
|
|
323
|
+
console.log(chalk.cyan.bold("Your Courses - Grades Summary"));
|
|
324
|
+
console.log(chalk.green(`✓ Found ${coursesWithGrades.length} course(s)${options.all ? " (including inactive)" : " (active only)"}.`));
|
|
241
325
|
const colNo = Math.max(3, coursesWithGrades.length.toString().length + 1);
|
|
242
|
-
const colStatus = Math.max(8, ...coursesWithGrades.map(c => (c.course.workflow_state ===
|
|
243
|
-
|
|
326
|
+
const colStatus = Math.max(8, ...coursesWithGrades.map((c) => (c.course.workflow_state === "available"
|
|
327
|
+
? "Active"
|
|
328
|
+
: c.course.workflow_state || "Inactive").length));
|
|
329
|
+
const colCurrent = Math.max(9, ...coursesWithGrades.map((c) => {
|
|
244
330
|
const s = c.enrollment?.grades?.current_score;
|
|
245
331
|
return s !== null && s !== undefined ? `${s}%`.length : 3;
|
|
246
332
|
}));
|
|
247
|
-
const colFinal = Math.max(7, ...coursesWithGrades.map(c => {
|
|
333
|
+
const colFinal = Math.max(7, ...coursesWithGrades.map((c) => {
|
|
248
334
|
const s = c.enrollment?.grades?.final_score;
|
|
249
335
|
return s !== null && s !== undefined ? `${s}%`.length : 3;
|
|
250
336
|
}));
|
|
251
337
|
const terminalWidth = process.stdout.columns || 80;
|
|
252
338
|
const overhead = 16;
|
|
253
339
|
const availableForName = Math.max(20, terminalWidth - (colNo + colStatus + colCurrent + colFinal + overhead));
|
|
254
|
-
const maxNameLength = Math.max(11, ...coursesWithGrades.map(c => c.course.name.length));
|
|
340
|
+
const maxNameLength = Math.max(11, ...coursesWithGrades.map((c) => c.course.name.length));
|
|
255
341
|
const colName = Math.min(maxNameLength, availableForName);
|
|
256
|
-
console.log(chalk.gray(
|
|
257
|
-
chalk.gray(
|
|
258
|
-
chalk.gray(
|
|
259
|
-
chalk.gray(
|
|
260
|
-
chalk.gray(
|
|
261
|
-
|
|
262
|
-
chalk.
|
|
263
|
-
chalk.
|
|
264
|
-
chalk.
|
|
265
|
-
chalk.
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
chalk.
|
|
269
|
-
chalk.gray(
|
|
270
|
-
chalk.
|
|
342
|
+
console.log(chalk.gray("╭─") +
|
|
343
|
+
chalk.gray("─".repeat(colNo)) +
|
|
344
|
+
chalk.gray("┬─") +
|
|
345
|
+
chalk.gray("─".repeat(colName)) +
|
|
346
|
+
chalk.gray("┬─") +
|
|
347
|
+
chalk.gray("─".repeat(colStatus)) +
|
|
348
|
+
chalk.gray("┬─") +
|
|
349
|
+
chalk.gray("─".repeat(colCurrent)) +
|
|
350
|
+
chalk.gray("┬─") +
|
|
351
|
+
chalk.gray("─".repeat(colFinal)) +
|
|
352
|
+
chalk.gray("╮"));
|
|
353
|
+
console.log(chalk.gray("│ ") +
|
|
354
|
+
chalk.cyan.bold(pad("#", colNo)) +
|
|
355
|
+
chalk.gray("│ ") +
|
|
356
|
+
chalk.cyan.bold(pad("Course Name", colName)) +
|
|
357
|
+
chalk.gray("│ ") +
|
|
358
|
+
chalk.cyan.bold(pad("Status", colStatus)) +
|
|
359
|
+
chalk.gray("│ ") +
|
|
360
|
+
chalk.cyan.bold(pad("Current", colCurrent)) +
|
|
361
|
+
chalk.gray("│ ") +
|
|
362
|
+
chalk.cyan.bold(pad("Final", colFinal)) +
|
|
363
|
+
chalk.gray("│"));
|
|
364
|
+
console.log(chalk.gray("├─") +
|
|
365
|
+
chalk.gray("─".repeat(colNo)) +
|
|
366
|
+
chalk.gray("┼─") +
|
|
367
|
+
chalk.gray("─".repeat(colName)) +
|
|
368
|
+
chalk.gray("┼─") +
|
|
369
|
+
chalk.gray("─".repeat(colStatus)) +
|
|
370
|
+
chalk.gray("┼─") +
|
|
371
|
+
chalk.gray("─".repeat(colCurrent)) +
|
|
372
|
+
chalk.gray("┼─") +
|
|
373
|
+
chalk.gray("─".repeat(colFinal)) +
|
|
374
|
+
chalk.gray("┤"));
|
|
271
375
|
coursesWithGrades.forEach((item, index) => {
|
|
272
376
|
const { course, enrollment } = item;
|
|
273
377
|
const grades = enrollment?.grades;
|
|
274
378
|
const currentScore = grades?.current_score !== null && grades?.current_score !== undefined
|
|
275
379
|
? `${grades.current_score}%`
|
|
276
|
-
:
|
|
380
|
+
: "N/A";
|
|
277
381
|
const finalScore = grades?.final_score !== null && grades?.final_score !== undefined
|
|
278
382
|
? `${grades.final_score}%`
|
|
279
|
-
:
|
|
280
|
-
const statusText = course.workflow_state ===
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
383
|
+
: "N/A";
|
|
384
|
+
const statusText = course.workflow_state === "available"
|
|
385
|
+
? "Active"
|
|
386
|
+
: course.workflow_state || "Inactive";
|
|
387
|
+
const statusColored = course.workflow_state === "available"
|
|
388
|
+
? chalk.green(pad(statusText, colStatus))
|
|
389
|
+
: chalk.gray(pad(statusText, colStatus));
|
|
284
390
|
let displayName = course.name;
|
|
285
391
|
if (displayName.length > colName) {
|
|
286
|
-
displayName = displayName.substring(0, colName - 3) +
|
|
392
|
+
displayName = displayName.substring(0, colName - 3) + "...";
|
|
287
393
|
}
|
|
288
|
-
console.log(chalk.gray(
|
|
289
|
-
chalk.white(pad(
|
|
290
|
-
|
|
291
|
-
chalk.white(pad(
|
|
292
|
-
chalk.
|
|
394
|
+
console.log(chalk.gray("│ ") +
|
|
395
|
+
chalk.white(pad((index + 1).toString(), colNo)) +
|
|
396
|
+
chalk.gray("│ ") +
|
|
397
|
+
chalk.white(pad(displayName, colName)) +
|
|
398
|
+
chalk.gray("│ ") +
|
|
399
|
+
statusColored +
|
|
400
|
+
chalk.gray("│ ") +
|
|
401
|
+
chalk.white(pad(currentScore, colCurrent)) +
|
|
402
|
+
chalk.gray("│ ") +
|
|
403
|
+
chalk.white(pad(finalScore, colFinal)) +
|
|
404
|
+
chalk.gray("│"));
|
|
293
405
|
});
|
|
294
|
-
console.log(chalk.gray(
|
|
295
|
-
chalk.gray(
|
|
296
|
-
chalk.gray(
|
|
297
|
-
chalk.gray(
|
|
298
|
-
chalk.gray(
|
|
299
|
-
|
|
300
|
-
|
|
406
|
+
console.log(chalk.gray("╰─") +
|
|
407
|
+
chalk.gray("─".repeat(colNo)) +
|
|
408
|
+
chalk.gray("┴─") +
|
|
409
|
+
chalk.gray("─".repeat(colName)) +
|
|
410
|
+
chalk.gray("┴─") +
|
|
411
|
+
chalk.gray("─".repeat(colStatus)) +
|
|
412
|
+
chalk.gray("┴─") +
|
|
413
|
+
chalk.gray("─".repeat(colCurrent)) +
|
|
414
|
+
chalk.gray("┴─") +
|
|
415
|
+
chalk.gray("─".repeat(colFinal)) +
|
|
416
|
+
chalk.gray("╯"));
|
|
417
|
+
console.log(chalk.yellow("\nSelect a course to view detailed grades for all assignments."));
|
|
418
|
+
console.log(chalk.gray(" Or enter 0 to exit."));
|
|
301
419
|
if (!options.all) {
|
|
302
|
-
console.log(chalk.gray(
|
|
420
|
+
console.log(chalk.gray(" Tip: Use --all flag to include inactive courses.\n"));
|
|
303
421
|
}
|
|
304
422
|
else {
|
|
305
423
|
console.log();
|
|
@@ -308,11 +426,11 @@ export async function showGrades(courseId, options = {}) {
|
|
|
308
426
|
const num = parseInt(input);
|
|
309
427
|
return !isNaN(num) && num >= 0 && num <= coursesWithGrades.length;
|
|
310
428
|
};
|
|
311
|
-
const answer = await askQuestionWithValidation(rl, chalk.bold.cyan(
|
|
429
|
+
const answer = await askQuestionWithValidation(rl, chalk.bold.cyan("Enter course number: "), validator, chalk.red(`Please enter a number between 0 and ${coursesWithGrades.length}.`));
|
|
312
430
|
const choice = parseInt(answer);
|
|
313
431
|
rl.close();
|
|
314
432
|
if (choice === 0) {
|
|
315
|
-
console.log(chalk.yellow(
|
|
433
|
+
console.log(chalk.yellow("\nExiting grades viewer."));
|
|
316
434
|
return;
|
|
317
435
|
}
|
|
318
436
|
const selectedCourse = coursesWithGrades[choice - 1];
|
|
@@ -324,7 +442,7 @@ export async function showGrades(courseId, options = {}) {
|
|
|
324
442
|
}
|
|
325
443
|
catch (error) {
|
|
326
444
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
327
|
-
console.error(chalk.red(
|
|
445
|
+
console.error(chalk.red("Error fetching grades:"), errorMessage);
|
|
328
446
|
process.exit(1);
|
|
329
447
|
}
|
|
330
448
|
}
|