canvaslms-cli 1.4.6 → 1.4.7

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 (81) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +1 -9
  3. package/dist/commands/announcements.d.ts +3 -0
  4. package/dist/commands/announcements.d.ts.map +1 -0
  5. package/dist/commands/announcements.js +87 -0
  6. package/dist/commands/announcements.js.map +1 -0
  7. package/dist/commands/api.d.ts +3 -0
  8. package/dist/commands/api.d.ts.map +1 -0
  9. package/dist/commands/api.js +8 -0
  10. package/dist/commands/api.js.map +1 -0
  11. package/dist/commands/assignments.d.ts +3 -0
  12. package/dist/commands/assignments.d.ts.map +1 -0
  13. package/dist/commands/assignments.js +100 -0
  14. package/dist/commands/assignments.js.map +1 -0
  15. package/dist/commands/config.d.ts +6 -0
  16. package/dist/commands/config.d.ts.map +1 -0
  17. package/dist/commands/config.js +202 -0
  18. package/dist/commands/config.js.map +1 -0
  19. package/dist/commands/grades.d.ts +3 -0
  20. package/dist/commands/grades.d.ts.map +1 -0
  21. package/dist/commands/grades.js +128 -0
  22. package/dist/commands/grades.js.map +1 -0
  23. package/dist/commands/list.d.ts +3 -0
  24. package/dist/commands/list.d.ts.map +1 -0
  25. package/dist/commands/list.js +61 -0
  26. package/dist/commands/list.js.map +1 -0
  27. package/dist/commands/profile.d.ts +6 -0
  28. package/dist/commands/profile.d.ts.map +1 -0
  29. package/dist/commands/profile.js +30 -0
  30. package/dist/commands/profile.js.map +1 -0
  31. package/dist/commands/submit.d.ts +8 -0
  32. package/dist/commands/submit.d.ts.map +1 -0
  33. package/dist/commands/submit.js +319 -0
  34. package/dist/commands/submit.js.map +1 -0
  35. package/dist/index.d.ts +15 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +15 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/lib/api-client.d.ts +2 -0
  40. package/dist/lib/api-client.d.ts.map +1 -0
  41. package/dist/lib/api-client.js +69 -0
  42. package/dist/lib/api-client.js.map +1 -0
  43. package/dist/lib/config-validator.d.ts +4 -0
  44. package/dist/lib/config-validator.d.ts.map +1 -0
  45. package/dist/lib/config-validator.js +38 -0
  46. package/dist/lib/config-validator.js.map +1 -0
  47. package/dist/lib/config.d.ts +10 -0
  48. package/dist/lib/config.d.ts.map +1 -0
  49. package/dist/lib/config.js +85 -0
  50. package/dist/lib/config.js.map +1 -0
  51. package/dist/lib/file-upload.d.ts +3 -0
  52. package/dist/lib/file-upload.d.ts.map +1 -0
  53. package/dist/lib/file-upload.js +52 -0
  54. package/dist/lib/file-upload.js.map +1 -0
  55. package/dist/lib/interactive.d.ts +16 -0
  56. package/dist/lib/interactive.d.ts.map +1 -0
  57. package/dist/lib/interactive.js +756 -0
  58. package/dist/lib/interactive.js.map +1 -0
  59. package/dist/src/index.d.ts +3 -0
  60. package/dist/src/index.d.ts.map +1 -0
  61. package/dist/src/index.js +89 -0
  62. package/dist/src/index.js.map +1 -0
  63. package/dist/types/index.d.ts +146 -0
  64. package/dist/types/index.d.ts.map +1 -0
  65. package/dist/types/index.js +2 -0
  66. package/dist/types/index.js.map +1 -0
  67. package/package.json +76 -71
  68. package/commands/announcements.js +0 -102
  69. package/commands/api.js +0 -19
  70. package/commands/assignments.js +0 -100
  71. package/commands/config.js +0 -239
  72. package/commands/grades.js +0 -201
  73. package/commands/list.js +0 -68
  74. package/commands/profile.js +0 -35
  75. package/commands/submit.js +0 -603
  76. package/lib/api-client.js +0 -75
  77. package/lib/config-validator.js +0 -60
  78. package/lib/config.js +0 -103
  79. package/lib/file-upload.js +0 -74
  80. package/lib/interactive.js +0 -889
  81. package/src/index.js +0 -120
@@ -1,100 +0,0 @@
1
- /**
2
- * Assignments command
3
- */
4
-
5
- import { makeCanvasRequest } from '../lib/api-client.js';
6
- import chalk from 'chalk';
7
-
8
- function pad(str, len) {
9
- return str + ' '.repeat(Math.max(0, len - str.length));
10
- }
11
-
12
- export async function listAssignments(courseId, options) {
13
- try {
14
- const course = await makeCanvasRequest('get', `courses/${courseId}`);
15
- const queryParams = ['include[]=submission', 'include[]=score_statistics', 'per_page=100'];
16
- console.log(chalk.cyan.bold('\n' + '-'.repeat(60)));
17
- console.log(chalk.cyan.bold('Loading assignments, please wait...'));
18
- const assignments = await makeCanvasRequest('get', `courses/${courseId}/assignments`, queryParams);
19
- if (!assignments || assignments.length === 0) {
20
- console.log(chalk.red(`Error: No assignments found for course: ${course.name}`));
21
- return;
22
- }
23
- let filteredAssignments = assignments;
24
- if (options.submitted) {
25
- filteredAssignments = assignments.filter(a => a.submission && a.submission.submitted_at);
26
- } else if (options.pending) {
27
- filteredAssignments = assignments.filter(a => !a.submission || !a.submission.submitted_at);
28
- }
29
- console.log(chalk.cyan.bold('\n' + '-'.repeat(60)));
30
- console.log(chalk.cyan.bold(`Assignments for: ${course.name}`));
31
- console.log(chalk.cyan('-'.repeat(60)));
32
- console.log(chalk.green(`Success: Found ${filteredAssignments.length} assignment(s).`));
33
- console.log(chalk.cyan('-'.repeat(60)));
34
- // Column headers
35
- console.log(
36
- pad(chalk.bold('No.'), 5) +
37
- pad(chalk.bold('Assignment Name'), 35) +
38
- pad(chalk.bold('ID'), 10) +
39
- pad(chalk.bold('Grade'), 12) +
40
- pad(chalk.bold('Due'), 22) +
41
- (options.verbose ? pad(chalk.bold('Status'), 12) : '')
42
- );
43
- console.log(chalk.cyan('-'.repeat(60)));
44
- filteredAssignments.forEach((assignment, index) => {
45
- const submission = assignment.submission;
46
- let gradeDisplay = '';
47
- if (submission && submission.score !== null && submission.score !== undefined) {
48
- const score = submission.score % 1 === 0 ? Math.round(submission.score) : submission.score;
49
- const total = assignment.points_possible || 0;
50
- gradeDisplay = `${score}/${total}`;
51
- } else if (submission && submission.excused) {
52
- gradeDisplay = 'Excused';
53
- } else if (submission && submission.missing) {
54
- gradeDisplay = 'Missing';
55
- } else if (assignment.points_possible) {
56
- gradeDisplay = `–/${assignment.points_possible}`;
57
- } else {
58
- gradeDisplay = 'N/A';
59
- }
60
- let line = pad(chalk.white((index + 1) + '.'), 5) +
61
- pad(assignment.name, 35) +
62
- pad(String(assignment.id), 10) +
63
- pad(gradeDisplay, 12) +
64
- pad(assignment.due_at ? new Date(assignment.due_at).toLocaleString() : 'No due date', 22);
65
- if (options.verbose) {
66
- let status = 'Not submitted';
67
- if (submission && submission.submitted_at) {
68
- status = 'Submitted';
69
- }
70
- line += pad(status, 12);
71
- }
72
- console.log(line);
73
- if (options.verbose) {
74
- if (assignment.description) {
75
- const cleanDescription = assignment.description
76
- .replace(/<[^>]*>/g, '')
77
- .replace(/\s+/g, ' ')
78
- .trim()
79
- .substring(0, 150);
80
- console.log(' ' + chalk.gray('Description: ') + cleanDescription + (cleanDescription.length === 150 ? '...' : ''));
81
- } else {
82
- console.log(' ' + chalk.gray('Description: N/A'));
83
- }
84
- console.log(' ' + chalk.gray('Submission Types: ') + (assignment.submission_types?.join(', ') || 'N/A'));
85
- console.log(' ' + chalk.gray('Published: ') + (assignment.published ? 'Yes' : 'No'));
86
- if (assignment.points_possible) {
87
- console.log(' ' + chalk.gray('Points Possible: ') + assignment.points_possible);
88
- }
89
- if (submission && submission.attempt) {
90
- console.log(' ' + chalk.gray('Attempt: ') + submission.attempt);
91
- }
92
- }
93
- console.log('');
94
- });
95
- console.log(chalk.cyan('-'.repeat(60)));
96
- } catch (error) {
97
- console.error(chalk.red('Error fetching assignments:'), error.message);
98
- process.exit(1);
99
- }
100
- }
@@ -1,239 +0,0 @@
1
- /**
2
- * Config command
3
- */
4
-
5
- import { configExists, readConfig, saveConfig, deleteConfig, getConfigPath } from '../lib/config.js';
6
- import { createReadlineInterface, askQuestion } from '../lib/interactive.js';
7
- import chalk from 'chalk';
8
-
9
- export async function showConfig() {
10
- console.log(chalk.cyan.bold('\n' + '-'.repeat(60)));
11
- console.log(chalk.cyan.bold('Canvas CLI Configuration'));
12
- console.log(chalk.cyan('-'.repeat(60)));
13
-
14
- const configPath = getConfigPath();
15
- const hasConfig = configExists();
16
-
17
- console.log(chalk.white('Configuration file: ') + configPath);
18
- console.log(chalk.white('Status: ') + (hasConfig ? chalk.green('Found') : chalk.red('Not found')) + '\n');
19
-
20
- if (hasConfig) {
21
- const config = readConfig();
22
- if (config) {
23
- console.log(chalk.cyan('Current configuration:'));
24
- console.log(chalk.white(' Canvas Domain: ') + (config.domain || 'Not set'));
25
- console.log(chalk.white(' API Token: ') + (config.token ? config.token.substring(0, 10) + '...' : 'Not set'));
26
- console.log(chalk.white(' Created: ') + (config.createdAt ? new Date(config.createdAt).toLocaleString() : 'Unknown'));
27
- console.log(chalk.white(' Last Updated: ') + (config.lastUpdated ? new Date(config.lastUpdated).toLocaleString() : 'Unknown'));
28
- } } else {
29
- console.log(chalk.yellow('No configuration found.'));
30
- }
31
-
32
- console.log(chalk.cyan('\nAvailable commands:'));
33
- console.log(chalk.white(' canvas config setup # Interactive setup wizard'));
34
- console.log(chalk.white(' canvas config edit # Edit existing configuration'));
35
- console.log(chalk.white(' canvas config show # Show current configuration'));
36
- console.log(chalk.white(' canvas config delete # Delete configuration file'));
37
- console.log(chalk.white(' canvas config path # Show configuration file path'));
38
-
39
- console.log(chalk.cyan('\nManual setup:'));
40
- console.log(chalk.white('1. Get your Canvas API token:'));
41
- console.log(chalk.white(' - Log into your Canvas instance'));
42
- console.log(chalk.white(' - Go to Account → Settings'));
43
- console.log(chalk.white(' - Scroll down to "Approved Integrations"'));
44
- console.log(chalk.white(' - Click "+ New Access Token"'));
45
- console.log(chalk.white(' - Copy the generated token'));
46
- console.log(chalk.white('2. Run "canvas config setup" to configure'));
47
-
48
- console.log(chalk.cyan('\nExample usage after setup:'));
49
- console.log(chalk.white(' canvas list # List starred courses'));
50
- console.log(chalk.white(' canvas list -a # List all enrolled courses'));
51
- console.log(chalk.white(' canvas submit # Interactive assignment submission'));
52
- console.log(chalk.white(' canvas profile # Show user profile'));
53
- console.log(chalk.white(' canvas assignments 12345 # Show assignments for course'));
54
- console.log(chalk.white(' canvas grades # Show grades for all courses'));
55
- }
56
-
57
- export async function setupConfig() {
58
- const rl = createReadlineInterface();
59
-
60
- try {
61
- console.log(chalk.cyan.bold('\n' + '-'.repeat(60)));
62
- console.log(chalk.cyan.bold('Canvas CLI Configuration Setup'));
63
- console.log(chalk.cyan('-'.repeat(60)));
64
-
65
- // Check if config already exists
66
- if (configExists()) {
67
- const config = readConfig();
68
- console.log(chalk.yellow('Existing configuration found:'));
69
- console.log(chalk.white(' Domain: ') + (config.domain || 'Not set'));
70
- console.log(chalk.white(' Token: ') + (config.token ? 'Set (hidden)' : 'Not set') + '\n');
71
-
72
- const overwrite = await askQuestion(rl, 'Do you want to overwrite the existing configuration? (y/N): ');
73
- if (overwrite.toLowerCase() !== 'y' && overwrite.toLowerCase() !== 'yes') {
74
- console.log(chalk.yellow('Setup cancelled.'));
75
- return;
76
- }
77
- console.log('');
78
- }
79
- // Get Canvas domain
80
- const currentConfig = readConfig();
81
- let domain = await askQuestion(rl, `Enter your Canvas domain${currentConfig?.domain ? ` (${currentConfig.domain})` : ''}: `);
82
- if (!domain && currentConfig?.domain) {
83
- domain = currentConfig.domain;
84
- }
85
-
86
- if (!domain) {
87
- console.log(chalk.red('Canvas domain is required.'));
88
- return;
89
- }
90
-
91
- // Validate and clean domain
92
- domain = domain.replace(/^https?:\/\//, '').replace(/\/$/, '');
93
- if (!domain.includes('.')) {
94
- console.log(chalk.red('Invalid domain format. Please enter a valid domain (e.g., school.instructure.com)'));
95
- return;
96
- }
97
-
98
- console.log(chalk.green(`Domain set to: ${domain}\n`));
99
- // Get API token
100
- const defaultToken = currentConfig?.token || '';
101
- let token = await askQuestion(rl, `Enter your Canvas API token${defaultToken ? ' (press Enter to keep current)' : ''}: `);
102
- if (!token && defaultToken) {
103
- token = defaultToken;
104
- }
105
-
106
- if (!token) {
107
- console.log(chalk.red('Canvas API token is required.'));
108
- console.log(chalk.cyan('\nTo get your API token:'));
109
- console.log(chalk.white('1. Log into your Canvas instance'));
110
- console.log(chalk.white('2. Go to Account → Settings'));
111
- console.log(chalk.white('3. Scroll down to "Approved Integrations"'));
112
- console.log(chalk.white('4. Click "+ New Access Token"'));
113
- console.log(chalk.white('5. Copy the generated token'));
114
- return;
115
- }
116
-
117
- // Validate token format (basic check)
118
- if (token.length < 10) {
119
- console.log(chalk.red('API token seems too short. Please check your token.'));
120
- return;
121
- }
122
-
123
- console.log('Token received\n');
124
-
125
- // Save configuration
126
- const saved = saveConfig(domain, token);
127
- if (saved) {
128
- console.log(chalk.green('Success: Configuration setup completed successfully!'));
129
- console.log(chalk.cyan('\nNext steps:'));
130
- console.log(chalk.white(' canvas list # Test your setup by listing courses'));
131
- console.log(chalk.white(' canvas profile # View your profile'));
132
- console.log(chalk.white(' canvas config show # View your configuration'));
133
- }
134
-
135
- } catch (error) {
136
- console.error(chalk.red('Error: Setup failed: ') + error.message);
137
- } finally {
138
- rl.close();
139
- }
140
- }
141
-
142
- export async function editConfig() {
143
- const rl = createReadlineInterface();
144
-
145
- try {
146
- if (!configExists()) {
147
- console.log('No configuration file found. Run "canvas config setup" first.');
148
- return;
149
- }
150
-
151
- const config = readConfig();
152
- console.log(chalk.cyan.bold('\n' + '-'.repeat(60)));
153
- console.log(chalk.cyan.bold('Edit Canvas CLI Configuration'));
154
- console.log(chalk.cyan('-'.repeat(60)));
155
- console.log(chalk.cyan('Current values:'));
156
- console.log(chalk.white(' Domain: ') + config.domain);
157
- console.log(chalk.white(' Token: ') + (config.token ? config.token.substring(0, 10) + '...' : 'Not set') + '\n');
158
-
159
- // Edit domain
160
- const newDomain = await askQuestion(rl, `New Canvas domain (${config.domain}): `);
161
- const domain = newDomain.trim() || config.domain;
162
-
163
- // Edit token
164
- const changeToken = await askQuestion(rl, 'Change API token? (y/N): ');
165
- let token = config.token;
166
-
167
- if (changeToken.toLowerCase() === 'y' || changeToken.toLowerCase() === 'yes') {
168
- const newToken = await askQuestion(rl, 'New API token: ');
169
- if (newToken.trim()) {
170
- token = newToken.trim();
171
- }
172
- }
173
-
174
- // Confirm changes
175
- console.log(chalk.cyan('New configuration:'));
176
- console.log(chalk.white(' Domain: ') + domain);
177
- console.log(chalk.white(' Token: ') + (token ? token.substring(0, 10) + '...' : 'Not set'));
178
-
179
- const confirm = await askQuestion(rl, '\nSave changes? (Y/n): ');
180
- if (confirm.toLowerCase() === 'n' || confirm.toLowerCase() === 'no') {
181
- console.log(chalk.yellow('Changes cancelled.'));
182
- return;
183
- }
184
-
185
- const saved = saveConfig(domain, token);
186
- if (saved) {
187
- console.log(chalk.green('Success: Configuration updated successfully!'));
188
- }
189
-
190
- } catch (error) {
191
- console.error(chalk.red('Error: Edit failed: ') + error.message);
192
- } finally {
193
- rl.close();
194
- }
195
- }
196
-
197
- export function showConfigPath() {
198
- console.log(chalk.cyan('Configuration file location: ') + getConfigPath());
199
- console.log(chalk.white('Exists: ') + (configExists() ? chalk.green('Yes') : chalk.red('No')));
200
- }
201
-
202
- export function deleteConfigFile() {
203
- console.log(chalk.cyan.bold('\n' + '-'.repeat(60)));
204
- console.log(chalk.cyan.bold('Delete Configuration'));
205
- console.log(chalk.cyan('-'.repeat(60)));
206
-
207
- if (!configExists()) {
208
- console.log(chalk.yellow('No configuration file found.'));
209
- return;
210
- }
211
-
212
- const config = readConfig();
213
- console.log(chalk.cyan('Current configuration:'));
214
- console.log(chalk.white(' Domain: ') + config.domain);
215
- console.log(chalk.white(' Token: ') + (config.token ? 'Set (hidden)' : 'Not set'));
216
- console.log(chalk.white(' File: ') + getConfigPath() + '\n');
217
-
218
- // For safety, require explicit confirmation
219
- console.log(chalk.red('This will permanently delete your Canvas CLI configuration.'));
220
- console.log(chalk.yellow('You will need to run "canvas config setup" again to use the CLI.'));
221
- console.log(chalk.cyan('\nTo confirm deletion, type: DELETE'));
222
-
223
- const readline = require('readline');
224
- const rl = readline.createInterface({
225
- input: process.stdin,
226
- output: process.stdout
227
- });
228
-
229
- rl.question('Confirmation: ', (answer) => {
230
- if (answer === 'DELETE') {
231
- deleteConfig();
232
- console.log(chalk.green('Success: Configuration deleted.'));
233
- } else {
234
- console.log(chalk.yellow('Deletion cancelled.'));
235
- }
236
- rl.close();
237
- });
238
- }
239
-
@@ -1,201 +0,0 @@
1
- /**
2
- * Grades command
3
- */
4
-
5
- import { makeCanvasRequest } from '../lib/api-client.js';
6
- import { createReadlineInterface, askQuestion } from '../lib/interactive.js';
7
- import chalk from 'chalk';
8
-
9
- export async function showGrades(courseId, options) {
10
- const rl = createReadlineInterface();
11
-
12
- try {
13
- if (courseId) {
14
- // Get grades for specific course with assignment details
15
- await showCourseGrades(courseId, options, rl);
16
- } else {
17
- // Interactive course selection for grades
18
- await showInteractiveGrades(options, rl);
19
- }
20
-
21
- } catch (error) {
22
- console.error(chalk.red('Error fetching grades: ') + error.message);
23
- process.exit(1);
24
- } finally {
25
- rl.close();
26
- }
27
- }
28
-
29
- async function showCourseGrades(courseId, options, rl) {
30
- // Get course details
31
- const course = await makeCanvasRequest('get', `courses/${courseId}`);
32
-
33
- // Get overall enrollment grades
34
- const enrollments = await makeCanvasRequest('get', `courses/${courseId}/enrollments`, [
35
- 'user_id=self',
36
- 'include[]=grades'
37
- ]);
38
-
39
- if (!enrollments || enrollments.length === 0) {
40
- console.log(chalk.red('Error: No enrollment found for this course.'));
41
- return;
42
- }
43
-
44
- const enrollment = enrollments[0];
45
-
46
- console.log(chalk.cyan.bold('\n' + '-'.repeat(60)));
47
- console.log(chalk.cyan.bold('Grades for: ') + course.name);
48
- console.log(chalk.cyan('-'.repeat(60)));
49
- console.log(chalk.white('Course Overview:'));
50
- console.log(chalk.white(' Current Score: ') + (enrollment.grades?.current_score || 'N/A') + '%');
51
- console.log(chalk.white(' Final Score: ') + (enrollment.grades?.final_score || 'N/A') + '%');
52
- console.log(chalk.white(' Current Grade: ') + (enrollment.grades?.current_grade || 'N/A'));
53
- console.log(chalk.white(' Final Grade: ') + (enrollment.grades?.final_grade || 'N/A'));
54
-
55
- // Get assignment grades
56
- console.log(chalk.cyan('\nLoading assignment grades...'));
57
- const assignments = await makeCanvasRequest('get', `courses/${courseId}/assignments`, [
58
- 'include[]=submission',
59
- 'order_by=due_at',
60
- 'per_page=100'
61
- ]);
62
-
63
- if (!assignments || assignments.length === 0) {
64
- console.log(chalk.yellow('No assignments found for this course.'));
65
- return;
66
- }
67
-
68
- // Filter assignments with grades
69
- const gradedAssignments = assignments.filter(assignment => {
70
- const submission = assignment.submission;
71
- return submission && (
72
- submission.score !== null ||
73
- submission.excused ||
74
- submission.missing ||
75
- submission.grade
76
- );
77
- });
78
-
79
- if (gradedAssignments.length === 0) {
80
- console.log(chalk.yellow('No graded assignments found.'));
81
- return;
82
- }
83
-
84
- console.log(chalk.green('Success: Grades loaded.'));
85
- console.log(chalk.cyan.bold('\n' + '-'.repeat(60)));
86
- console.log(chalk.cyan.bold(`Assignment Grades (${gradedAssignments.length} graded):`));
87
- console.log(chalk.cyan('-'.repeat(60)));
88
- gradedAssignments.forEach((assignment, index) => {
89
- const submission = assignment.submission;
90
- let gradeDisplay = '';
91
- let gradeColor = chalk.white;
92
-
93
- if (submission.score !== null && submission.score !== undefined) {
94
- const score = submission.score % 1 === 0 ? Math.round(submission.score) : submission.score;
95
- const total = assignment.points_possible || 0;
96
- gradeDisplay = `${score}/${total} pts`;
97
-
98
- // Color coding based on percentage
99
- if (total > 0) {
100
- const percentage = (submission.score / total) * 100;
101
- if (percentage >= 90) gradeColor = chalk.green;
102
- else if (percentage >= 80) gradeColor = chalk.cyan;
103
- else if (percentage >= 70) gradeColor = chalk.yellow;
104
- else if (percentage >= 60) gradeColor = chalk.magenta;
105
- else gradeColor = chalk.red;
106
- }
107
-
108
- // Add letter grade if available
109
- if (submission.grade && isNaN(submission.grade)) {
110
- gradeDisplay = `${submission.grade} (${gradeDisplay})`;
111
- }
112
- } else if (submission.excused) {
113
- gradeDisplay = 'Excused';
114
- gradeColor = chalk.blue;
115
- } else if (submission.missing) {
116
- gradeDisplay = 'Missing';
117
- gradeColor = chalk.red;
118
- }
119
-
120
- console.log(chalk.white(`${index + 1}. ${assignment.name}`));
121
- console.log(' Grade: ' + gradeColor(gradeDisplay));
122
-
123
- if (submission.submitted_at) {
124
- console.log(' Submitted: ' + new Date(submission.submitted_at).toLocaleDateString());
125
- }
126
-
127
- if (assignment.due_at) {
128
- console.log(' Due: ' + new Date(assignment.due_at).toLocaleDateString());
129
- }
130
-
131
- if (options.verbose && submission.grader_comments) {
132
- console.log(' Comments: ' + submission.grader_comments);
133
- }
134
-
135
- console.log('');
136
- });
137
- }
138
-
139
- async function showInteractiveGrades(options, rl) {
140
- // Get all courses with enrollment information
141
- const courses = await makeCanvasRequest('get', 'courses', [
142
- 'enrollment_state=active',
143
- 'include[]=enrollments',
144
- 'include[]=favorites',
145
- 'include[]=total_scores'
146
- ]);
147
-
148
- if (!courses || courses.length === 0) {
149
- console.log(chalk.red('Error: No courses found.'));
150
- return;
151
- }
152
-
153
- // Move declaration above first use
154
- const coursesWithGrades = courses.filter(course =>
155
- course.enrollments && course.enrollments.length > 0
156
- );
157
-
158
- // Show courses overview first
159
- console.log(chalk.cyan.bold('\n' + '-'.repeat(60)));
160
- console.log(chalk.cyan.bold('Grades Overview'));
161
- console.log(chalk.cyan('-'.repeat(60)));
162
- console.log(chalk.white(`Found ${coursesWithGrades.length} enrolled course(s):\n`));
163
-
164
- coursesWithGrades.forEach((course, index) => {
165
- let gradeColor = chalk.gray;
166
- const enrollment = course.enrollments[0];
167
- const currentScore = enrollment.grades?.current_score;
168
- if (currentScore !== null && currentScore !== undefined) {
169
- if (currentScore >= 90) gradeColor = chalk.green;
170
- else if (currentScore >= 80) gradeColor = chalk.cyan;
171
- else if (currentScore >= 70) gradeColor = chalk.yellow;
172
- else if (currentScore >= 60) gradeColor = chalk.magenta;
173
- else gradeColor = chalk.red;
174
- }
175
- console.log(chalk.white(`${index + 1}. ${course.name}`));
176
- if (enrollment.grades) {
177
- console.log(' Current: ' + gradeColor(`${currentScore || 'N/A'}% (${enrollment.grades.current_grade || 'N/A'})`));
178
- } else {
179
- console.log(' Current: ' + chalk.gray('Grades not available'));
180
- }
181
- console.log('');
182
- });
183
-
184
- // Ask if user wants to see detailed grades for a specific course
185
- const viewDetailed = await askQuestion(rl, 'View detailed grades for a specific course? (Y/n): ');
186
-
187
- if (viewDetailed.toLowerCase() === 'n' || viewDetailed.toLowerCase() === 'no') {
188
- return;
189
- }
190
-
191
- const courseChoice = await askQuestion(rl, '\nEnter course number for detailed grades: ');
192
- const courseIndex = parseInt(courseChoice) - 1;
193
-
194
- if (courseIndex >= 0 && courseIndex < coursesWithGrades.length) {
195
- const selectedCourse = coursesWithGrades[courseIndex];
196
- console.log(chalk.green(`\nSelected: ${selectedCourse.name}`));
197
- await showCourseGrades(selectedCourse.id, options, rl);
198
- } else {
199
- console.log(chalk.red('Invalid course selection.'));
200
- }
201
- }
package/commands/list.js DELETED
@@ -1,68 +0,0 @@
1
- /**
2
- * List courses command
3
- */
4
-
5
- import { makeCanvasRequest } from '../lib/api-client.js';
6
- import chalk from 'chalk';
7
-
8
- function pad(str, len) {
9
- return str + ' '.repeat(Math.max(0, len - str.length));
10
- }
11
-
12
- export async function listCourses(options) {
13
- try {
14
- const queryParams = [];
15
- queryParams.push('enrollment_state=active');
16
- queryParams.push('include[]=term');
17
- queryParams.push('include[]=course_progress');
18
- queryParams.push('include[]=total_students');
19
- queryParams.push('include[]=favorites');
20
-
21
- console.log(chalk.cyan.bold('\n' + '-'.repeat(60)));
22
- console.log(chalk.cyan.bold('Loading courses, please wait...'));
23
- const courses = await makeCanvasRequest('get', 'courses', queryParams);
24
- if (!courses || courses.length === 0) {
25
- console.log(chalk.red('Error: No courses found.'));
26
- return;
27
- }
28
- let filteredCourses = courses;
29
- if (!options.all) {
30
- filteredCourses = courses.filter(course => course.is_favorite);
31
- if (filteredCourses.length === 0) {
32
- console.log(chalk.red('Error: No starred courses found. Use -a to see all courses.'));
33
- return;
34
- }
35
- }
36
- const courseLabel = options.all ? 'enrolled course(s)' : 'starred course(s)';
37
- console.log(chalk.green(`Success: Found ${filteredCourses.length} ${courseLabel}.`));
38
- console.log(chalk.cyan('-'.repeat(60)));
39
- // Column headers
40
- console.log(
41
- pad(chalk.bold('No.'), 5) +
42
- pad(chalk.bold('Course Name'), 35) +
43
- pad(chalk.bold('ID'), 10) +
44
- pad(chalk.bold('Code'), 12) +
45
- (options.verbose ? pad(chalk.bold('Term'), 15) + pad(chalk.bold('Students'), 10) + pad(chalk.bold('Start'), 12) + pad(chalk.bold('End'), 12) + pad(chalk.bold('State'), 12) + pad(chalk.bold('Progress'), 18) : '')
46
- );
47
- console.log(chalk.cyan('-'.repeat(60)));
48
- filteredCourses.forEach((course, index) => {
49
- let line = pad(chalk.white((index + 1) + '.'), 5) +
50
- pad(course.name, 35) +
51
- pad(String(course.id), 10) +
52
- pad(course.course_code || 'N/A', 12);
53
- if (options.verbose) {
54
- line += pad(course.term?.name || 'N/A', 15) +
55
- pad(String(course.total_students || 'N/A'), 10) +
56
- pad(course.start_at ? new Date(course.start_at).toLocaleDateString() : 'N/A', 12) +
57
- pad(course.end_at ? new Date(course.end_at).toLocaleDateString() : 'N/A', 12) +
58
- pad(course.workflow_state, 12) +
59
- pad(course.course_progress ? `${course.course_progress.requirement_completed_count || 0}/${course.course_progress.requirement_count || 0}` : 'N/A', 18);
60
- }
61
- console.log(line);
62
- });
63
- console.log(chalk.cyan('-'.repeat(60)));
64
- } catch (error) {
65
- console.error(chalk.red('Error fetching courses:'), error.message);
66
- process.exit(1);
67
- }
68
- }
@@ -1,35 +0,0 @@
1
- /**
2
- * Profile command
3
- */
4
-
5
- import { makeCanvasRequest } from '../lib/api-client.js';
6
- import chalk from 'chalk';
7
-
8
- export async function showProfile(options) {
9
- try {
10
- console.log(chalk.cyan.bold('Loading profile, please wait...'));
11
- const user = await makeCanvasRequest('get', 'users/self', ['include[]=email', 'include[]=locale']);
12
-
13
- console.log(chalk.cyan.bold('\n' + '-'.repeat(60)));
14
- console.log(chalk.cyan.bold('User Profile'));
15
- console.log(chalk.cyan('-'.repeat(60)));
16
- console.log(chalk.white('Name: ') + user.name);
17
- console.log(chalk.white('ID: ') + user.id);
18
- console.log(chalk.white('Email: ') + (user.email || 'N/A'));
19
- console.log(chalk.white('Login ID: ') + (user.login_id || 'N/A'));
20
-
21
- if (options.verbose) {
22
- console.log(chalk.white('Short Name: ') + (user.short_name || 'N/A'));
23
- console.log(chalk.white('Sortable Name: ') + (user.sortable_name || 'N/A'));
24
- console.log(chalk.white('Locale: ') + (user.locale || 'N/A'));
25
- console.log(chalk.white('Time Zone: ') + (user.time_zone || 'N/A'));
26
- console.log(chalk.white('Avatar URL: ') + (user.avatar_url || 'N/A'));
27
- console.log(chalk.white('Created: ') + (user.created_at ? new Date(user.created_at).toLocaleString() : 'N/A'));
28
- }
29
- console.log(chalk.green('Success: Profile loaded.'));
30
-
31
- } catch (error) {
32
- console.error(chalk.red('Error: Failed to fetch profile: ') + error.message);
33
- process.exit(1);
34
- }
35
- }