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.
Files changed (69) hide show
  1. package/CHANGELOG.md +210 -186
  2. package/README.md +131 -129
  3. package/dist/commands/announcements.d.ts +2 -2
  4. package/dist/commands/announcements.d.ts.map +1 -1
  5. package/dist/commands/announcements.js +43 -124
  6. package/dist/commands/announcements.js.map +1 -1
  7. package/dist/commands/api.d.ts +1 -1
  8. package/dist/commands/api.d.ts.map +1 -1
  9. package/dist/commands/api.js +1 -1
  10. package/dist/commands/api.js.map +1 -1
  11. package/dist/commands/assignments.d.ts +2 -2
  12. package/dist/commands/assignments.d.ts.map +1 -1
  13. package/dist/commands/assignments.js +40 -128
  14. package/dist/commands/assignments.js.map +1 -1
  15. package/dist/commands/config.d.ts.map +1 -1
  16. package/dist/commands/config.js +102 -85
  17. package/dist/commands/config.js.map +1 -1
  18. package/dist/commands/grades.d.ts +2 -2
  19. package/dist/commands/grades.d.ts.map +1 -1
  20. package/dist/commands/grades.js +296 -178
  21. package/dist/commands/grades.js.map +1 -1
  22. package/dist/commands/list.d.ts +1 -1
  23. package/dist/commands/list.d.ts.map +1 -1
  24. package/dist/commands/list.js +26 -46
  25. package/dist/commands/list.js.map +1 -1
  26. package/dist/commands/profile.d.ts.map +1 -1
  27. package/dist/commands/profile.js +57 -32
  28. package/dist/commands/profile.js.map +1 -1
  29. package/dist/commands/submit.d.ts +2 -2
  30. package/dist/commands/submit.d.ts.map +1 -1
  31. package/dist/commands/submit.js +110 -165
  32. package/dist/commands/submit.js.map +1 -1
  33. package/dist/index.d.ts +14 -14
  34. package/dist/index.js +14 -14
  35. package/dist/lib/api-client.d.ts +3 -0
  36. package/dist/lib/api-client.d.ts.map +1 -1
  37. package/dist/lib/api-client.js +31 -16
  38. package/dist/lib/api-client.js.map +1 -1
  39. package/dist/lib/config-validator.d.ts.map +1 -1
  40. package/dist/lib/config-validator.js +12 -12
  41. package/dist/lib/config-validator.js.map +1 -1
  42. package/dist/lib/config.d.ts +1 -1
  43. package/dist/lib/config.d.ts.map +1 -1
  44. package/dist/lib/config.js +16 -14
  45. package/dist/lib/config.js.map +1 -1
  46. package/dist/lib/display.d.ts +84 -0
  47. package/dist/lib/display.d.ts.map +1 -0
  48. package/dist/lib/display.js +603 -0
  49. package/dist/lib/display.js.map +1 -0
  50. package/dist/lib/file-upload.d.ts.map +1 -1
  51. package/dist/lib/file-upload.js +17 -17
  52. package/dist/lib/file-upload.js.map +1 -1
  53. package/dist/lib/interactive.d.ts +1 -1
  54. package/dist/lib/interactive.d.ts.map +1 -1
  55. package/dist/lib/interactive.js +200 -143
  56. package/dist/lib/interactive.js.map +1 -1
  57. package/dist/src/index.d.ts +0 -1
  58. package/dist/src/index.js +62 -62
  59. package/dist/src/index.js.map +1 -1
  60. package/dist/types/index.d.ts +1 -1
  61. package/package.json +9 -4
  62. package/dist/commands/coursenames.d.ts +0 -15
  63. package/dist/commands/coursenames.d.ts.map +0 -1
  64. package/dist/commands/coursenames.js +0 -150
  65. package/dist/commands/coursenames.js.map +0 -1
  66. package/dist/lib/course-utils.d.ts +0 -6
  67. package/dist/lib/course-utils.d.ts.map +0 -1
  68. package/dist/lib/course-utils.js +0 -17
  69. package/dist/lib/course-utils.js.map +0 -1
@@ -1,32 +1,30 @@
1
- import { makeCanvasRequest } from '../lib/api-client.js';
2
- import { createReadlineInterface, askQuestionWithValidation } from '../lib/interactive.js';
3
- import chalk from 'chalk';
4
- function pad(str, len) {
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('\n' + '='.repeat(80)));
9
- console.log(chalk.cyan.bold('Loading course grades, please wait...'));
10
- const course = await makeCanvasRequest('get', `courses/${courseId}`);
11
- const enrollmentParams = ['user_id=self', 'include[]=total_scores'];
12
- enrollmentParams.push('state[]=active');
13
- enrollmentParams.push('state[]=invited');
14
- enrollmentParams.push('state[]=creation_pending');
15
- enrollmentParams.push('state[]=rejected');
16
- enrollmentParams.push('state[]=completed');
17
- enrollmentParams.push('state[]=inactive');
18
- const enrollments = await makeCanvasRequest('get', `courses/${courseId}/enrollments`, enrollmentParams);
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('Error: No enrollment found for this course.'));
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('get', `courses/${courseId}/assignments`, ['include[]=submission', 'per_page=100']);
26
- console.log(chalk.cyan.bold('\n' + '='.repeat(80)));
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('='.repeat(80)));
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
- : 'N/A';
47
- console.log(chalk.white.bold('\nOverall Grades:'));
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.40));
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('╭─') + chalk.gray('─'.repeat(colMetric)) + chalk.gray('┬─') +
55
- chalk.gray(''.repeat(colScore)) + chalk.gray('┬─') +
56
- chalk.gray('─'.repeat(colStatus)) + chalk.gray('╮'));
57
- console.log(chalk.gray('│ ') + chalk.cyan.bold(pad('Metric', colMetric)) + chalk.gray('│ ') +
58
- chalk.cyan.bold(pad('Score/Grade', colScore)) + chalk.gray('│ ') +
59
- chalk.cyan.bold(pad('Status', colStatus)) + chalk.gray('│'));
60
- console.log(chalk.gray('├─') + chalk.gray('─'.repeat(colMetric)) + chalk.gray('┼─') +
61
- chalk.gray('─'.repeat(colScore)) + chalk.gray('┼─') +
62
- chalk.gray('─'.repeat(colStatus)) + chalk.gray('┤'));
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}%` : 'N/A';
65
- const finalScoreValue = grades.final_score !== null ? `${grades.final_score}%` : 'N/A';
66
- const currentGradeValue = grades.current_grade || 'N/A';
67
- const finalGradeValue = grades.final_grade || 'N/A';
68
- console.log(chalk.gray('') + chalk.white(pad('Current Score', colMetric)) + chalk.gray('│ ') +
69
- chalk.green.bold(pad(currentScoreValue, colScore)) + chalk.gray('│ ') +
70
- chalk.gray(pad('Official', colStatus)) + chalk.gray('│'));
71
- console.log(chalk.gray('│ ') + chalk.white(pad('Final Score', colMetric)) + chalk.gray('│ ') +
72
- chalk.green.bold(pad(finalScoreValue, colScore)) + chalk.gray('') +
73
- chalk.gray(pad('Official', colStatus)) + chalk.gray('│'));
74
- console.log(chalk.gray(' ') + chalk.white(pad('Current Grade', colMetric)) + chalk.gray('│ ') +
75
- chalk.green.bold(pad(currentGradeValue, colScore)) + chalk.gray('') +
76
- chalk.gray(pad('Letter Grade', colStatus)) + chalk.gray('│'));
77
- console.log(chalk.gray('') + chalk.white(pad('Final Grade', colMetric)) + chalk.gray('│ ') +
78
- chalk.green.bold(pad(finalGradeValue, colScore)) + chalk.gray('│ ') +
79
- chalk.gray(pad('Letter Grade', colStatus)) + chalk.gray('│'));
80
- console.log(chalk.gray('├─') + chalk.gray('─'.repeat(colMetric)) + chalk.gray('┼─') +
81
- chalk.gray('─'.repeat(colScore)) + chalk.gray('┼─') +
82
- chalk.gray('─'.repeat(colStatus)) + chalk.gray('┤'));
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('') + chalk.white(pad('Graded Assignments', colMetric)) + chalk.gray('│ ') +
85
- chalk.cyan.bold(pad(`${gradedAssignments.length} / ${assignments.length}`, colScore)) + chalk.gray('│ ') +
86
- chalk.gray(pad('Completed', colStatus)) + chalk.gray('│'));
87
- console.log(chalk.gray('│ ') + chalk.white(pad('Points Earned', colMetric)) + chalk.gray('│ ') +
88
- chalk.cyan.bold(pad(`${totalPointsEarned.toFixed(2)} / ${totalPointsPossible.toFixed(2)}`, colScore)) + chalk.gray('') +
89
- chalk.gray(pad('Total', colStatus)) + chalk.gray('│'));
90
- console.log(chalk.gray(' ') + chalk.white(pad('Calculated Average', colMetric)) + chalk.gray('│ ') +
91
- chalk.cyan.bold(pad(typeof calculatedPercentage === 'string' ? calculatedPercentage : `${calculatedPercentage}%`, colScore)) + chalk.gray('') +
92
- chalk.gray(pad('From Graded', colStatus)) + chalk.gray('│'));
93
- console.log(chalk.gray('╰─') + chalk.gray('─'.repeat(colMetric)) + chalk.gray('┴─') +
94
- chalk.gray('─'.repeat(colScore)) + chalk.gray('┴─') +
95
- chalk.gray('─'.repeat(colStatus)) + chalk.gray('╯'));
96
- console.log(chalk.white.bold('\nAssignment Breakdown:'));
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(' No assignments found for this course.'));
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('╭─') + chalk.gray('─'.repeat(colNo)) + chalk.gray('┬─') +
110
- chalk.gray(''.repeat(colName)) + chalk.gray('┬─') +
111
- chalk.gray('─'.repeat(colScore)) + chalk.gray('┬─') +
112
- chalk.gray(''.repeat(colStatus)) + chalk.gray('┬─') +
113
- chalk.gray('─'.repeat(colDate)) + chalk.gray('╮'));
114
- console.log(chalk.gray('│ ') + chalk.cyan.bold(pad('#', colNo)) + chalk.gray('│ ') +
115
- chalk.cyan.bold(pad('Assignment Name', colName)) + chalk.gray('│ ') +
116
- chalk.cyan.bold(pad('Score', colScore)) + chalk.gray('│ ') +
117
- chalk.cyan.bold(pad('Status', colStatus)) + chalk.gray('│ ') +
118
- chalk.cyan.bold(pad('Due Date', colDate)) + chalk.gray('│'));
119
- console.log(chalk.gray('├─') + chalk.gray('─'.repeat(colNo)) + chalk.gray('┼─') +
120
- chalk.gray('─'.repeat(colName)) + chalk.gray('┼─') +
121
- chalk.gray('─'.repeat(colScore)) + chalk.gray('┼─') +
122
- chalk.gray('─'.repeat(colStatus)) + chalk.gray('┼─') +
123
- chalk.gray('─'.repeat(colDate)) + chalk.gray('┤'));
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
- : 'N/A';
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 = '✓ Graded';
203
+ statusDisplay = "✓ Graded";
138
204
  statusColor = chalk.green;
139
205
  }
140
206
  else if (percentage >= 50) {
141
- statusDisplay = '✓ Graded';
207
+ statusDisplay = "✓ Graded";
142
208
  statusColor = chalk.yellow;
143
209
  }
144
210
  else {
145
- statusDisplay = '✓ Graded';
211
+ statusDisplay = "✓ Graded";
146
212
  statusColor = chalk.red;
147
213
  }
148
214
  }
149
215
  else if (assignment.submitted) {
150
- statusDisplay = 'Pending';
216
+ statusDisplay = "Pending";
151
217
  statusColor = chalk.cyan;
152
218
  }
153
219
  else {
154
- statusDisplay = 'Not Done';
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
- : 'No due date';
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('') + chalk.white(pad((index + 1).toString(), colNo)) + chalk.gray('│ ') +
165
- chalk.white(pad(displayName, colName)) + chalk.gray('│ ') +
166
- chalk.white(pad(scoreDisplay, colScore)) + chalk.gray('') +
167
- statusColor(pad(statusDisplay, colStatus)) + chalk.gray('│ ') +
168
- chalk.gray(pad(dueDate, colDate)) + 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('╰─') + chalk.gray('─'.repeat(colNo)) + chalk.gray('┴─') +
171
- chalk.gray(''.repeat(colName)) + chalk.gray('┴─') +
172
- chalk.gray('─'.repeat(colScore)) + chalk.gray('┴─') +
173
- chalk.gray(''.repeat(colStatus)) + chalk.gray('┴─') +
174
- chalk.gray('─'.repeat(colDate)) + 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('='.repeat(80)));
254
+ console.log(chalk.cyan("=".repeat(80)));
177
255
  if (options.verbose && enrollment) {
178
- console.log(chalk.cyan('\n' + '-'.repeat(80)));
179
- console.log(chalk.cyan.bold('Enrollment Details:'));
180
- console.log(chalk.white(' Enrollment ID: ') + enrollment.id);
181
- console.log(chalk.white(' Type: ') + enrollment.type);
182
- console.log(chalk.white(' State: ') + enrollment.enrollment_state);
183
- console.log(chalk.cyan('-'.repeat(80)));
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(courseId, options = {}) {
264
+ export async function showGrades(courseName, options = {}) {
187
265
  try {
188
- if (courseId) {
189
- await showDetailedGrades(courseId, options);
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('\n' + '='.repeat(80)));
194
- console.log(chalk.cyan.bold('Loading your courses, please wait...'));
195
- const enrollmentState = options.all ? undefined : 'active';
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
- 'include[]=total_scores',
198
- 'include[]=current_grading_period_scores',
199
- 'per_page=100'
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('get', 'courses', queryParams);
288
+ let courses = await makeCanvasRequest("get", "courses", queryParams);
205
289
  if (!courses || courses.length === 0) {
206
- console.log(chalk.red('Error: No courses found.'));
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('Error: No courses found (after filtering).'));
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 = ['user_id=self', 'include[]=total_scores'];
219
- enrollmentParams.push('state[]=active');
220
- enrollmentParams.push('state[]=invited');
221
- enrollmentParams.push('state[]=creation_pending');
222
- enrollmentParams.push('state[]=rejected');
223
- enrollmentParams.push('state[]=completed');
224
- enrollmentParams.push('state[]=inactive');
225
- const enrollments = await makeCanvasRequest('get', `courses/${course.id}/enrollments`, enrollmentParams);
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 (error) {
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('\n' + '='.repeat(80)));
239
- console.log(chalk.cyan.bold('Your Courses - Grades Summary'));
240
- console.log(chalk.green(`✓ Found ${coursesWithGrades.length} course(s)${options.all ? ' (including inactive)' : ' (active only)'}.`));
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 === 'available' ? 'Active' : (c.course.workflow_state || 'Inactive')).length));
243
- const colCurrent = Math.max(9, ...coursesWithGrades.map(c => {
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('╭─') + chalk.gray('─'.repeat(colNo)) + chalk.gray('┬─') +
257
- chalk.gray(''.repeat(colName)) + chalk.gray('┬─') +
258
- chalk.gray('─'.repeat(colStatus)) + chalk.gray('┬─') +
259
- chalk.gray(''.repeat(colCurrent)) + chalk.gray('┬─') +
260
- chalk.gray('─'.repeat(colFinal)) + chalk.gray('╮'));
261
- console.log(chalk.gray('│ ') + chalk.cyan.bold(pad('#', colNo)) + chalk.gray('│ ') +
262
- chalk.cyan.bold(pad('Course Name', colName)) + chalk.gray('│ ') +
263
- chalk.cyan.bold(pad('Status', colStatus)) + chalk.gray('│ ') +
264
- chalk.cyan.bold(pad('Current', colCurrent)) + chalk.gray('│ ') +
265
- chalk.cyan.bold(pad('Final', colFinal)) + chalk.gray('│'));
266
- console.log(chalk.gray('├─') + chalk.gray('─'.repeat(colNo)) + chalk.gray('┼─') +
267
- chalk.gray('─'.repeat(colName)) + chalk.gray('┼─') +
268
- chalk.gray('─'.repeat(colStatus)) + chalk.gray('┼─') +
269
- chalk.gray('─'.repeat(colCurrent)) + chalk.gray('┼─') +
270
- chalk.gray('─'.repeat(colFinal)) + chalk.gray('┤'));
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
- : 'N/A';
380
+ : "N/A";
277
381
  const finalScore = grades?.final_score !== null && grades?.final_score !== undefined
278
382
  ? `${grades.final_score}%`
279
- : 'N/A';
280
- const statusText = course.workflow_state === 'available' ? 'Active' : (course.workflow_state || 'Inactive');
281
- const statusColored = course.workflow_state === 'available' ?
282
- chalk.green(pad(statusText, colStatus)) :
283
- chalk.gray(pad(statusText, colStatus));
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('') + chalk.white(pad((index + 1).toString(), colNo)) + chalk.gray('│ ') +
289
- chalk.white(pad(displayName, colName)) + chalk.gray('│ ') +
290
- statusColored + chalk.gray('') +
291
- chalk.white(pad(currentScore, colCurrent)) + chalk.gray('│ ') +
292
- chalk.white(pad(finalScore, colFinal)) + chalk.gray('│'));
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('╰─') + chalk.gray('─'.repeat(colNo)) + chalk.gray('┴─') +
295
- chalk.gray(''.repeat(colName)) + chalk.gray('┴─') +
296
- chalk.gray('─'.repeat(colStatus)) + chalk.gray('┴─') +
297
- chalk.gray(''.repeat(colCurrent)) + chalk.gray('┴─') +
298
- chalk.gray('─'.repeat(colFinal)) + chalk.gray('╯'));
299
- console.log(chalk.yellow('\nSelect a course to view detailed grades for all assignments.'));
300
- console.log(chalk.gray(' Or enter 0 to exit.'));
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(' Tip: Use --all flag to include inactive courses.\n'));
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('Enter course number: '), validator, chalk.red(`Please enter a number between 0 and ${coursesWithGrades.length}.`));
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('\nExiting grades viewer.'));
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('Error fetching grades:'), errorMessage);
445
+ console.error(chalk.red("Error fetching grades:"), errorMessage);
328
446
  process.exit(1);
329
447
  }
330
448
  }