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