canvaslms-cli 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +83 -0
- package/LICENSE +21 -0
- package/README.md +193 -0
- package/commands/announcements.js +77 -0
- package/commands/api.js +19 -0
- package/commands/assignments.js +126 -0
- package/commands/config.js +242 -0
- package/commands/grades.js +68 -0
- package/commands/list.js +70 -0
- package/commands/profile.js +34 -0
- package/commands/submit.js +366 -0
- package/lib/api-client.js +79 -0
- package/lib/config-validator.js +63 -0
- package/lib/config.js +150 -0
- package/lib/file-upload.js +78 -0
- package/lib/interactive.js +31 -0
- package/package.json +67 -0
- package/src/index.js +142 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.1.0] - 2025-07-03
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Home directory configuration system (~/.canvaslms-cli-config.json)
|
|
13
|
+
- Interactive configuration setup wizard (`canvas config setup`)
|
|
14
|
+
- Configuration management subcommands:
|
|
15
|
+
- `canvas config show` - Display current configuration
|
|
16
|
+
- `canvas config edit` - Edit existing configuration
|
|
17
|
+
- `canvas config path` - Show config file location
|
|
18
|
+
- `canvas config delete` - Remove configuration file
|
|
19
|
+
- Automatic configuration validation for all commands
|
|
20
|
+
- Improved error handling and user guidance
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- **BREAKING**: Removed environment variable support (.env files)
|
|
25
|
+
- Configuration now stored in user's home directory instead of project directory
|
|
26
|
+
- Enhanced configuration validation with better error messages
|
|
27
|
+
- Improved user onboarding with guided setup process
|
|
28
|
+
|
|
29
|
+
### Removed
|
|
30
|
+
|
|
31
|
+
- dotenv dependency (no longer needed)
|
|
32
|
+
- Environment variable fallback support
|
|
33
|
+
|
|
34
|
+
## [1.0.0] - 2025-07-03
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
|
|
38
|
+
- Initial release of Canvas CLI Tool
|
|
39
|
+
- Modular architecture with separate command handlers
|
|
40
|
+
- Interactive assignment submission with file upload
|
|
41
|
+
- Course management (list starred/all courses)
|
|
42
|
+
- Assignment operations (view, filter by status)
|
|
43
|
+
- Grade viewing for all courses or specific course
|
|
44
|
+
- Announcements viewing
|
|
45
|
+
- User profile management
|
|
46
|
+
- Raw API access for all HTTP methods (GET, POST, PUT, DELETE)
|
|
47
|
+
- Comprehensive configuration management
|
|
48
|
+
- Color-coded output for better readability
|
|
49
|
+
- Support for multiple file uploads
|
|
50
|
+
- File selection from current directory
|
|
51
|
+
- Detailed verbose modes for all commands
|
|
52
|
+
|
|
53
|
+
### Features
|
|
54
|
+
|
|
55
|
+
- 📚 Course listing with favorites support
|
|
56
|
+
- 📝 Assignment management with status indicators
|
|
57
|
+
- 🚀 Interactive file submission workflow
|
|
58
|
+
- 📢 Announcement viewing
|
|
59
|
+
- 👤 Profile information display
|
|
60
|
+
- 🔧 Direct Canvas API access
|
|
61
|
+
- ⚡ Command aliases for faster usage
|
|
62
|
+
- 🎨 Color-coded grade display
|
|
63
|
+
- 📁 Smart file selection interface
|
|
64
|
+
|
|
65
|
+
### Technical
|
|
66
|
+
|
|
67
|
+
- Modular command structure in separate files
|
|
68
|
+
- Reusable API client library
|
|
69
|
+
- Interactive prompt utilities
|
|
70
|
+
- File upload handling with progress indicators
|
|
71
|
+
- Error handling and user-friendly messages
|
|
72
|
+
- Cross-platform compatibility
|
|
73
|
+
|
|
74
|
+
## [Unreleased]
|
|
75
|
+
|
|
76
|
+
### Planned Features
|
|
77
|
+
|
|
78
|
+
- Assignment creation and editing
|
|
79
|
+
- Bulk operations
|
|
80
|
+
- Plugin system
|
|
81
|
+
- Advanced filtering options
|
|
82
|
+
- Export functionality
|
|
83
|
+
- Offline mode support
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Canvas CLI Tool
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Canvas CLI Tool
|
|
2
|
+
|
|
3
|
+
A powerful command-line interface for interacting with Canvas LMS API. This tool allows you to manage courses, assignments, submissions, and more directly from your terminal.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 📚 **Course Management**: List starred and enrolled courses
|
|
8
|
+
- 📝 **Assignment Operations**: View assignments, grades, and submission status
|
|
9
|
+
- 🚀 **File Submission**: Interactive file upload for assignments (single or multiple files)
|
|
10
|
+
- 📢 **Announcements**: View course announcements
|
|
11
|
+
- 👤 **Profile Management**: View user profile information
|
|
12
|
+
- 🔧 **Raw API Access**: Direct access to Canvas API endpoints
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
### Global Installation (Recommended)
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install -g canvas-cli-tool
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Local Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install canvas-cli-tool
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Setup
|
|
29
|
+
|
|
30
|
+
1. **Get your Canvas API token**:
|
|
31
|
+
- Log into your Canvas instance
|
|
32
|
+
- Go to Account → Settings
|
|
33
|
+
- Scroll down to "Approved Integrations"
|
|
34
|
+
- Click "+ New Access Token"
|
|
35
|
+
- Copy the generated token
|
|
36
|
+
|
|
37
|
+
2. **Configure the CLI**:
|
|
38
|
+
```bash
|
|
39
|
+
canvas config
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
3. **Create a `.env` file** in your project root:
|
|
43
|
+
```env
|
|
44
|
+
CANVAS_DOMAIN=your-canvas-domain.instructure.com
|
|
45
|
+
CANVAS_API_TOKEN=your-api-token
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Usage
|
|
49
|
+
|
|
50
|
+
### Basic Commands
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# Show configuration help
|
|
54
|
+
canvas config
|
|
55
|
+
|
|
56
|
+
# List starred courses (default)
|
|
57
|
+
canvas list
|
|
58
|
+
|
|
59
|
+
# List all enrolled courses
|
|
60
|
+
canvas list -a
|
|
61
|
+
|
|
62
|
+
# List courses with detailed information
|
|
63
|
+
canvas list -v
|
|
64
|
+
|
|
65
|
+
# Show user profile
|
|
66
|
+
canvas profile
|
|
67
|
+
|
|
68
|
+
# Show detailed profile information
|
|
69
|
+
canvas profile -v
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Assignment Operations
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# List assignments for a course
|
|
76
|
+
canvas assignments 12345
|
|
77
|
+
|
|
78
|
+
# Show detailed assignment information
|
|
79
|
+
canvas assignments 12345 -v
|
|
80
|
+
|
|
81
|
+
# Show only submitted assignments
|
|
82
|
+
canvas assignments 12345 -s
|
|
83
|
+
|
|
84
|
+
# Show only pending assignments
|
|
85
|
+
canvas assignments 12345 -p
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### File Submission
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# Interactive assignment submission
|
|
92
|
+
canvas submit
|
|
93
|
+
|
|
94
|
+
# Submit with specific course ID
|
|
95
|
+
canvas submit -c 12345
|
|
96
|
+
|
|
97
|
+
# Submit with specific assignment ID
|
|
98
|
+
canvas submit -a 67890
|
|
99
|
+
|
|
100
|
+
# Submit specific file
|
|
101
|
+
canvas submit -f myfile.pdf
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Grades and Announcements
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# Show grades for all courses
|
|
108
|
+
canvas grades
|
|
109
|
+
|
|
110
|
+
# Show grades for specific course
|
|
111
|
+
canvas grades 12345
|
|
112
|
+
|
|
113
|
+
# Show recent announcements
|
|
114
|
+
canvas announcements
|
|
115
|
+
|
|
116
|
+
# Show announcements for specific course
|
|
117
|
+
canvas announcements 12345
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Raw API Access
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# GET request
|
|
124
|
+
canvas get users/self
|
|
125
|
+
|
|
126
|
+
# GET with query parameters
|
|
127
|
+
canvas get courses -q enrollment_state=active
|
|
128
|
+
|
|
129
|
+
# POST request with data
|
|
130
|
+
canvas post courses/123/assignments -d '{"assignment": {"name": "Test"}}'
|
|
131
|
+
|
|
132
|
+
# POST with data from file
|
|
133
|
+
canvas post courses/123/assignments -d @assignment.json
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Command Reference
|
|
137
|
+
|
|
138
|
+
| Command | Alias | Description |
|
|
139
|
+
|---------|-------|-------------|
|
|
140
|
+
| `list` | `l` | List courses |
|
|
141
|
+
| `assignments` | `assign` | List assignments |
|
|
142
|
+
| `submit` | `sub` | Submit assignment files |
|
|
143
|
+
| `grades` | `grade` | Show grades |
|
|
144
|
+
| `announcements` | `announce` | Show announcements |
|
|
145
|
+
| `profile` | `me` | Show user profile |
|
|
146
|
+
| `config` | - | Show configuration |
|
|
147
|
+
| `get` | `g` | GET API request |
|
|
148
|
+
| `post` | `p` | POST API request |
|
|
149
|
+
| `put` | - | PUT API request |
|
|
150
|
+
| `delete` | `d` | DELETE API request |
|
|
151
|
+
|
|
152
|
+
## File Structure
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
canvas-cli-tool/
|
|
156
|
+
├── src/
|
|
157
|
+
│ └── index.js # Main CLI entry point
|
|
158
|
+
├── lib/
|
|
159
|
+
│ ├── api-client.js # Canvas API client
|
|
160
|
+
│ ├── config.js # Configuration management
|
|
161
|
+
│ ├── file-upload.js # File upload utilities
|
|
162
|
+
│ └── interactive.js # Interactive prompt utilities
|
|
163
|
+
├── commands/
|
|
164
|
+
│ ├── list.js # List courses command
|
|
165
|
+
│ ├── assignments.js # Assignments command
|
|
166
|
+
│ ├── submit.js # Submit command
|
|
167
|
+
│ ├── grades.js # Grades command
|
|
168
|
+
│ ├── announcements.js # Announcements command
|
|
169
|
+
│ ├── profile.js # Profile command
|
|
170
|
+
│ ├── config.js # Config command
|
|
171
|
+
│ └── api.js # Raw API commands
|
|
172
|
+
└── package.json
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Requirements
|
|
176
|
+
|
|
177
|
+
- Node.js >= 14.0.0
|
|
178
|
+
- npm >= 6.0.0
|
|
179
|
+
- Valid Canvas LMS access with API token
|
|
180
|
+
|
|
181
|
+
## Contributing
|
|
182
|
+
|
|
183
|
+
1. Fork the repository
|
|
184
|
+
2. Create a feature branch: `git checkout -b feature-name`
|
|
185
|
+
3. Make your changes
|
|
186
|
+
4. Run tests: `npm test`
|
|
187
|
+
5. Commit your changes: `git commit -am 'Add feature'`
|
|
188
|
+
6. Push to the branch: `git push origin feature-name`
|
|
189
|
+
7. Submit a pull request
|
|
190
|
+
|
|
191
|
+
## License
|
|
192
|
+
|
|
193
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Announcements command
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { makeCanvasRequest } = require('../lib/api-client');
|
|
6
|
+
|
|
7
|
+
async function showAnnouncements(courseId, options) {
|
|
8
|
+
try {
|
|
9
|
+
if (courseId) {
|
|
10
|
+
// Get announcements for specific course
|
|
11
|
+
const announcements = await makeCanvasRequest('get', `courses/${courseId}/discussion_topics`, [
|
|
12
|
+
'only_announcements=true',
|
|
13
|
+
`per_page=${options.limit}`
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
if (!announcements || announcements.length === 0) {
|
|
17
|
+
console.log('No announcements found for this course.');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
console.log(`Recent ${announcements.length} announcement(s) for course ${courseId}:\n`);
|
|
22
|
+
|
|
23
|
+
announcements.forEach((announcement, index) => {
|
|
24
|
+
console.log(`${index + 1}. ${announcement.title}`);
|
|
25
|
+
console.log(` Posted: ${announcement.posted_at ? new Date(announcement.posted_at).toLocaleString() : 'N/A'}`);
|
|
26
|
+
console.log(` Author: ${announcement.author?.display_name || 'Unknown'}`);
|
|
27
|
+
|
|
28
|
+
if (announcement.message) {
|
|
29
|
+
// Remove HTML tags and truncate
|
|
30
|
+
const message = announcement.message.replace(/<[^>]*>/g, '').substring(0, 200);
|
|
31
|
+
console.log(` Message: ${message}${announcement.message.length > 200 ? '...' : ''}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
console.log('');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
} else {
|
|
38
|
+
// Get announcements for all enrolled courses
|
|
39
|
+
const courses = await makeCanvasRequest('get', 'courses', ['enrollment_state=active']);
|
|
40
|
+
|
|
41
|
+
if (!courses || courses.length === 0) {
|
|
42
|
+
console.log('No courses found.');
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
console.log('Getting announcements from all enrolled courses...\n');
|
|
47
|
+
|
|
48
|
+
for (const course of courses.slice(0, 3)) { // Limit to first 3 courses to avoid too many requests
|
|
49
|
+
try {
|
|
50
|
+
const announcements = await makeCanvasRequest('get', `courses/${course.id}/discussion_topics`, [
|
|
51
|
+
'only_announcements=true',
|
|
52
|
+
'per_page=2'
|
|
53
|
+
]);
|
|
54
|
+
|
|
55
|
+
if (announcements && announcements.length > 0) {
|
|
56
|
+
console.log(`📢 ${course.name}:`);
|
|
57
|
+
announcements.forEach((announcement) => {
|
|
58
|
+
console.log(` • ${announcement.title} (${announcement.posted_at ? new Date(announcement.posted_at).toLocaleDateString() : 'No date'})`);
|
|
59
|
+
});
|
|
60
|
+
console.log('');
|
|
61
|
+
}
|
|
62
|
+
} catch (error) {
|
|
63
|
+
// Skip courses that don't allow access to announcements
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error('Error fetching announcements:', error.message);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
module.exports = {
|
|
76
|
+
showAnnouncements
|
|
77
|
+
};
|
package/commands/api.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raw API commands (get, post, put, delete, query)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { makeCanvasRequest } = require('../lib/api-client');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Create query command handler
|
|
9
|
+
*/
|
|
10
|
+
function createQueryHandler(method) {
|
|
11
|
+
return async function(endpoint, options) {
|
|
12
|
+
const data = await makeCanvasRequest(method, endpoint, options.query, options.data);
|
|
13
|
+
console.log(JSON.stringify(data, null, 2));
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = {
|
|
18
|
+
createQueryHandler
|
|
19
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assignments command
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { makeCanvasRequest } = require('../lib/api-client');
|
|
6
|
+
|
|
7
|
+
async function listAssignments(courseId, options) {
|
|
8
|
+
try {
|
|
9
|
+
const queryParams = ['include[]=submission', 'include[]=score_statistics', 'per_page=100'];
|
|
10
|
+
|
|
11
|
+
const assignments = await makeCanvasRequest('get', `courses/${courseId}/assignments`, queryParams);
|
|
12
|
+
|
|
13
|
+
if (!assignments || assignments.length === 0) {
|
|
14
|
+
console.log('No assignments found for this course.');
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Filter assignments based on options
|
|
19
|
+
let filteredAssignments = assignments;
|
|
20
|
+
if (options.submitted) {
|
|
21
|
+
filteredAssignments = assignments.filter(a => a.submission && a.submission.submitted_at);
|
|
22
|
+
} else if (options.pending) {
|
|
23
|
+
filteredAssignments = assignments.filter(a => !a.submission || !a.submission.submitted_at);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
console.log(`Found ${filteredAssignments.length} assignment(s):\n`);
|
|
27
|
+
|
|
28
|
+
filteredAssignments.forEach((assignment, index) => {
|
|
29
|
+
const submission = assignment.submission;
|
|
30
|
+
const isSubmitted = submission && submission.submitted_at;
|
|
31
|
+
const submissionStatus = isSubmitted ? '✅' : '❌';
|
|
32
|
+
|
|
33
|
+
// Format grade like Canvas web interface with enhanced formatting
|
|
34
|
+
let gradeDisplay = '';
|
|
35
|
+
let gradeColor = '';
|
|
36
|
+
|
|
37
|
+
if (submission && submission.score !== null && submission.score !== undefined) {
|
|
38
|
+
// Format score to remove unnecessary decimals (e.g., 3.0 becomes 3)
|
|
39
|
+
const score = submission.score % 1 === 0 ? Math.round(submission.score) : submission.score;
|
|
40
|
+
const total = assignment.points_possible || 0;
|
|
41
|
+
gradeDisplay = `${score}/${total}`;
|
|
42
|
+
|
|
43
|
+
// Add color coding based on score percentage
|
|
44
|
+
if (total > 0) {
|
|
45
|
+
const percentage = (submission.score / total) * 100;
|
|
46
|
+
if (percentage >= 90) gradeColor = '\x1b[32m'; // Green for A
|
|
47
|
+
else if (percentage >= 80) gradeColor = '\x1b[36m'; // Cyan for B
|
|
48
|
+
else if (percentage >= 70) gradeColor = '\x1b[33m'; // Yellow for C
|
|
49
|
+
else if (percentage >= 60) gradeColor = '\x1b[35m'; // Magenta for D
|
|
50
|
+
else gradeColor = '\x1b[31m'; // Red for F
|
|
51
|
+
}
|
|
52
|
+
} else if (submission && submission.excused) {
|
|
53
|
+
gradeDisplay = 'Excused';
|
|
54
|
+
gradeColor = '\x1b[34m'; // Blue for excused
|
|
55
|
+
} else if (submission && submission.missing) {
|
|
56
|
+
gradeDisplay = 'Missing';
|
|
57
|
+
gradeColor = '\x1b[31m'; // Red for missing
|
|
58
|
+
} else if (assignment.points_possible) {
|
|
59
|
+
gradeDisplay = `–/${assignment.points_possible}`;
|
|
60
|
+
gradeColor = '\x1b[90m'; // Gray for not graded yet
|
|
61
|
+
} else {
|
|
62
|
+
gradeDisplay = 'N/A';
|
|
63
|
+
gradeColor = '\x1b[90m'; // Gray for N/A
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Handle letter grades if present
|
|
67
|
+
if (submission && submission.grade && isNaN(submission.grade)) {
|
|
68
|
+
gradeDisplay = submission.grade + (assignment.points_possible ? ` (${gradeDisplay})` : '');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
console.log(`${index + 1}. ${submissionStatus} ${assignment.name}`);
|
|
72
|
+
console.log(` ID: ${assignment.id}`);
|
|
73
|
+
console.log(` Grade: ${gradeColor}${gradeDisplay} pts\x1b[0m`);
|
|
74
|
+
console.log(` Due: ${assignment.due_at ? new Date(assignment.due_at).toLocaleString() : 'No due date'}`);
|
|
75
|
+
|
|
76
|
+
if (isSubmitted) {
|
|
77
|
+
console.log(` Submitted: ${new Date(submission.submitted_at).toLocaleString()}`);
|
|
78
|
+
if (submission.workflow_state) {
|
|
79
|
+
// Color code submission status
|
|
80
|
+
let statusColor = '';
|
|
81
|
+
switch(submission.workflow_state) {
|
|
82
|
+
case 'graded': statusColor = '\x1b[32m'; break; // Green
|
|
83
|
+
case 'submitted': statusColor = '\x1b[33m'; break; // Yellow
|
|
84
|
+
case 'pending_review': statusColor = '\x1b[36m'; break; // Cyan
|
|
85
|
+
default: statusColor = '\x1b[37m'; // White
|
|
86
|
+
}
|
|
87
|
+
console.log(` Status: ${statusColor}${submission.workflow_state}\x1b[0m`);
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
console.log(` Status: \x1b[31mNot submitted\x1b[0m`); // Red for not submitted
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (options.verbose) {
|
|
94
|
+
if (assignment.description) {
|
|
95
|
+
// Strip HTML tags and clean up description
|
|
96
|
+
const cleanDescription = assignment.description
|
|
97
|
+
.replace(/<[^>]*>/g, '') // Remove HTML tags
|
|
98
|
+
.replace(/\s+/g, ' ') // Replace multiple whitespace with single space
|
|
99
|
+
.trim() // Remove leading/trailing whitespace
|
|
100
|
+
.substring(0, 150); // Limit to 150 characters
|
|
101
|
+
console.log(` Description: ${cleanDescription}${cleanDescription.length === 150 ? '...' : ''}`);
|
|
102
|
+
} else {
|
|
103
|
+
console.log(` Description: N/A`);
|
|
104
|
+
}
|
|
105
|
+
console.log(` Submission Types: ${assignment.submission_types?.join(', ') || 'N/A'}`);
|
|
106
|
+
console.log(` Published: ${assignment.published ? 'Yes' : 'No'}`);
|
|
107
|
+
if (assignment.points_possible) {
|
|
108
|
+
console.log(` Points Possible: ${assignment.points_possible}`);
|
|
109
|
+
}
|
|
110
|
+
if (submission && submission.attempt) {
|
|
111
|
+
console.log(` Attempt: ${submission.attempt}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
console.log('');
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
} catch (error) {
|
|
119
|
+
console.error('Error fetching assignments:', error.message);
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
module.exports = {
|
|
125
|
+
listAssignments
|
|
126
|
+
};
|