@mo7yw4ng/openape 1.0.4 → 1.0.6
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/README.md +11 -8
- package/esm/deno.js +1 -1
- package/esm/src/commands/announcements.d.ts.map +1 -1
- package/esm/src/commands/announcements.js +22 -85
- package/esm/src/commands/assignments.d.ts.map +1 -1
- package/esm/src/commands/assignments.js +2 -3
- package/esm/src/commands/auth.d.ts.map +1 -1
- package/esm/src/commands/auth.js +24 -14
- package/esm/src/commands/calendar.d.ts.map +1 -1
- package/esm/src/commands/calendar.js +32 -84
- package/esm/src/commands/courses.d.ts.map +1 -1
- package/esm/src/commands/courses.js +2 -38
- package/esm/src/commands/forums.d.ts.map +1 -1
- package/esm/src/commands/forums.js +47 -175
- package/esm/src/commands/grades.d.ts.map +1 -1
- package/esm/src/commands/grades.js +10 -47
- package/esm/src/commands/materials.d.ts.map +1 -1
- package/esm/src/commands/materials.js +135 -223
- package/esm/src/commands/quizzes.d.ts.map +1 -1
- package/esm/src/commands/quizzes.js +32 -56
- package/esm/src/commands/skills.js +3 -3
- package/esm/src/commands/upload.d.ts.map +1 -1
- package/esm/src/commands/upload.js +2 -5
- package/esm/src/commands/videos.d.ts.map +1 -1
- package/esm/src/commands/videos.js +6 -76
- package/esm/src/index.d.ts +2 -1
- package/esm/src/index.d.ts.map +1 -1
- package/esm/src/index.js +5 -1
- package/esm/src/lib/auth.d.ts +21 -2
- package/esm/src/lib/auth.d.ts.map +1 -1
- package/esm/src/lib/auth.js +79 -20
- package/esm/src/lib/logger.d.ts +2 -2
- package/esm/src/lib/logger.d.ts.map +1 -1
- package/esm/src/lib/logger.js +6 -4
- package/esm/src/lib/moodle.d.ts +18 -0
- package/esm/src/lib/moodle.d.ts.map +1 -1
- package/esm/src/lib/moodle.js +54 -2
- package/esm/src/lib/utils.d.ts +3 -8
- package/esm/src/lib/utils.d.ts.map +1 -1
- package/esm/src/lib/utils.js +3 -10
- package/package.json +1 -1
- package/script/deno.js +1 -1
- package/script/src/commands/announcements.d.ts.map +1 -1
- package/script/src/commands/announcements.js +23 -89
- package/script/src/commands/assignments.d.ts.map +1 -1
- package/script/src/commands/assignments.js +2 -3
- package/script/src/commands/auth.d.ts.map +1 -1
- package/script/src/commands/auth.js +24 -14
- package/script/src/commands/calendar.d.ts.map +1 -1
- package/script/src/commands/calendar.js +33 -85
- package/script/src/commands/courses.d.ts.map +1 -1
- package/script/src/commands/courses.js +9 -48
- package/script/src/commands/forums.d.ts.map +1 -1
- package/script/src/commands/forums.js +50 -181
- package/script/src/commands/grades.d.ts.map +1 -1
- package/script/src/commands/grades.js +14 -54
- package/script/src/commands/materials.d.ts.map +1 -1
- package/script/src/commands/materials.js +132 -220
- package/script/src/commands/quizzes.d.ts.map +1 -1
- package/script/src/commands/quizzes.js +40 -67
- package/script/src/commands/skills.js +3 -3
- package/script/src/commands/upload.d.ts.map +1 -1
- package/script/src/commands/upload.js +2 -5
- package/script/src/commands/videos.d.ts.map +1 -1
- package/script/src/commands/videos.js +11 -81
- package/script/src/index.d.ts +2 -1
- package/script/src/index.d.ts.map +1 -1
- package/script/src/index.js +5 -1
- package/script/src/lib/auth.d.ts +21 -2
- package/script/src/lib/auth.d.ts.map +1 -1
- package/script/src/lib/auth.js +83 -56
- package/script/src/lib/logger.d.ts +2 -2
- package/script/src/lib/logger.d.ts.map +1 -1
- package/script/src/lib/logger.js +6 -4
- package/script/src/lib/moodle.d.ts +18 -0
- package/script/src/lib/moodle.d.ts.map +1 -1
- package/script/src/lib/moodle.js +56 -2
- package/script/src/lib/utils.d.ts +3 -8
- package/script/src/lib/utils.d.ts.map +1 -1
- package/script/src/lib/utils.js +3 -11
- package/skills/openape/SKILL.md +10 -8
|
@@ -1,68 +1,28 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.registerForumsCommand = registerForumsCommand;
|
|
7
4
|
const utils_js_1 = require("../lib/utils.js");
|
|
8
5
|
const moodle_js_1 = require("../lib/moodle.js");
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
12
|
-
const node_fs_1 = __importDefault(require("node:fs"));
|
|
6
|
+
const auth_js_1 = require("../lib/auth.js");
|
|
7
|
+
const index_js_1 = require("../index.js");
|
|
13
8
|
function registerForumsCommand(program) {
|
|
14
9
|
const forumsCmd = program.command("forums");
|
|
15
10
|
forumsCmd.description("Forum operations");
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const opts = command?.optsWithGlobals ? command.optsWithGlobals() : options;
|
|
19
|
-
const outputFormat = (0, utils_js_1.getOutputFormat)(command || { optsWithGlobals: () => ({ output: "json" }) });
|
|
20
|
-
const silent = outputFormat === "json" && !opts.verbose;
|
|
21
|
-
const log = (0, logger_js_1.createLogger)(opts.verbose, silent);
|
|
22
|
-
const baseDir = (0, utils_js_1.getBaseDir)();
|
|
23
|
-
const sessionPath = node_path_1.default.resolve(baseDir, ".auth", "storage-state.json");
|
|
24
|
-
// Check if session exists
|
|
25
|
-
if (!node_fs_1.default.existsSync(sessionPath)) {
|
|
26
|
-
log.error("未找到登入 session。請先執行 'openape auth login' 進行登入。");
|
|
27
|
-
log.info(`Session 預期位置: ${sessionPath}`);
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
// Try to load WS token
|
|
31
|
-
const wsToken = (0, token_js_1.loadWsToken)(sessionPath);
|
|
32
|
-
if (!wsToken) {
|
|
33
|
-
log.error("未找到 WS token。請先執行 'openape auth login' 進行登入。");
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
// Try to load sesskey from cache
|
|
37
|
-
const sesskey = (0, token_js_1.loadSesskey)(sessionPath) || undefined;
|
|
38
|
-
return {
|
|
39
|
-
log,
|
|
40
|
-
session: {
|
|
41
|
-
wsToken,
|
|
42
|
-
moodleBaseUrl: "https://ilearning.cycu.edu.tw",
|
|
43
|
-
sesskey,
|
|
44
|
-
},
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
forumsCmd
|
|
48
|
-
.command("list")
|
|
49
|
-
.description("List forums from in-progress courses")
|
|
50
|
-
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
51
|
-
.action(async (options, command) => {
|
|
52
|
-
const apiContext = await createApiContext(options, command);
|
|
11
|
+
async function listForums(classification) {
|
|
12
|
+
const apiContext = await (0, auth_js_1.createApiContext)({});
|
|
53
13
|
if (!apiContext) {
|
|
54
14
|
process.exitCode = 1;
|
|
55
15
|
return;
|
|
56
16
|
}
|
|
57
17
|
const courses = await (0, moodle_js_1.getEnrolledCoursesApi)(apiContext.session, {
|
|
58
|
-
classification
|
|
18
|
+
classification,
|
|
59
19
|
});
|
|
60
|
-
// Get forums via WS API (no browser needed!)
|
|
61
20
|
const courseIds = courses.map(c => c.id);
|
|
62
21
|
const wsForums = await (0, moodle_js_1.getForumsApi)(apiContext.session, courseIds);
|
|
22
|
+
const courseMap = new Map(courses.map(c => [c.id, c]));
|
|
63
23
|
const allForums = [];
|
|
64
24
|
for (const wsForum of wsForums) {
|
|
65
|
-
const course =
|
|
25
|
+
const course = courseMap.get(wsForum.courseid);
|
|
66
26
|
if (course) {
|
|
67
27
|
allForums.push({
|
|
68
28
|
course_id: wsForum.courseid,
|
|
@@ -72,62 +32,22 @@ function registerForumsCommand(program) {
|
|
|
72
32
|
forum_id: wsForum.id,
|
|
73
33
|
name: wsForum.name,
|
|
74
34
|
timemodified: wsForum.timemodified,
|
|
75
|
-
// url: `https://ilearning.cycu.edu.tw/mod/forum/view.php?id=${wsForum.cmid}`,
|
|
76
35
|
});
|
|
77
36
|
}
|
|
78
37
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
for (const forum of allForums) {
|
|
86
|
-
console.log(JSON.stringify(forum));
|
|
87
|
-
}
|
|
88
|
-
});
|
|
38
|
+
(0, index_js_1.formatAndOutput)(allForums, "json", apiContext.log, { status: "success", timestamp: new Date().toISOString(), total_courses: courses.length, total_forums: allForums.length });
|
|
39
|
+
}
|
|
40
|
+
forumsCmd
|
|
41
|
+
.command("list")
|
|
42
|
+
.description("List forums from in-progress courses")
|
|
43
|
+
.action(() => listForums("inprogress"));
|
|
89
44
|
forumsCmd
|
|
90
45
|
.command("list-all")
|
|
91
46
|
.description("List all forums across all courses")
|
|
92
47
|
.option("--level <type>", "Course level: in_progress (default) | all", "in_progress")
|
|
93
|
-
.
|
|
94
|
-
.action(async (options, command) => {
|
|
95
|
-
const apiContext = await createApiContext(options, command);
|
|
96
|
-
if (!apiContext) {
|
|
97
|
-
process.exitCode = 1;
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
48
|
+
.action(async (options) => {
|
|
100
49
|
const classification = options.level === "all" ? undefined : "inprogress";
|
|
101
|
-
|
|
102
|
-
classification,
|
|
103
|
-
});
|
|
104
|
-
// Get forums via WS API (no browser needed!)
|
|
105
|
-
const courseIds = courses.map(c => c.id);
|
|
106
|
-
const wsForums = await (0, moodle_js_1.getForumsApi)(apiContext.session, courseIds);
|
|
107
|
-
const allForums = [];
|
|
108
|
-
for (const wsForum of wsForums) {
|
|
109
|
-
const course = courses.find(c => c.id === wsForum.courseid);
|
|
110
|
-
if (course) {
|
|
111
|
-
allForums.push({
|
|
112
|
-
course_id: wsForum.courseid,
|
|
113
|
-
course_name: course.fullname,
|
|
114
|
-
intro: wsForum.intro,
|
|
115
|
-
cmid: wsForum.cmid.toString(),
|
|
116
|
-
forum_id: wsForum.id,
|
|
117
|
-
name: wsForum.name,
|
|
118
|
-
timemodified: wsForum.timemodified,
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
console.log(JSON.stringify({
|
|
123
|
-
status: "success",
|
|
124
|
-
timestamp: new Date().toISOString(),
|
|
125
|
-
total_courses: courses.length,
|
|
126
|
-
total_forums: allForums.length,
|
|
127
|
-
}));
|
|
128
|
-
for (const forum of allForums) {
|
|
129
|
-
console.log(JSON.stringify(forum));
|
|
130
|
-
}
|
|
50
|
+
await listForums(classification);
|
|
131
51
|
});
|
|
132
52
|
forumsCmd
|
|
133
53
|
.command("discussions")
|
|
@@ -135,51 +55,29 @@ function registerForumsCommand(program) {
|
|
|
135
55
|
.argument("<forum-id>", "Forum ID")
|
|
136
56
|
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
137
57
|
.action(async (forumId, options, command) => {
|
|
138
|
-
const
|
|
58
|
+
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
59
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
139
60
|
if (!apiContext) {
|
|
140
61
|
process.exitCode = 1;
|
|
141
62
|
return;
|
|
142
63
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
});
|
|
147
|
-
// Get forums via WS API
|
|
148
|
-
const courseIds = courses.map(c => c.id);
|
|
149
|
-
const wsForums = await (0, moodle_js_1.getForumsApi)(apiContext.session, courseIds);
|
|
150
|
-
// Find forum by cmid or instance ID
|
|
151
|
-
const targetForum = wsForums.find(f => f.cmid.toString() === forumId || f.id === parseInt(forumId, 10));
|
|
152
|
-
if (!targetForum) {
|
|
153
|
-
console.log(JSON.stringify({ status: "error", error: "Forum not found" }));
|
|
64
|
+
const resolved = await (0, moodle_js_1.resolveForumId)(apiContext.session, forumId);
|
|
65
|
+
if (!resolved) {
|
|
66
|
+
apiContext.log.error("Forum not found");
|
|
154
67
|
process.exitCode = 1;
|
|
155
68
|
return;
|
|
156
69
|
}
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
course_name: course?.fullname,
|
|
169
|
-
total_discussions: discussions.length,
|
|
170
|
-
};
|
|
171
|
-
console.log(JSON.stringify(meta));
|
|
172
|
-
for (const d of discussions) {
|
|
173
|
-
console.log(JSON.stringify({
|
|
174
|
-
id: d.id,
|
|
175
|
-
name: d.name,
|
|
176
|
-
user_id: d.userId,
|
|
177
|
-
time_modified: d.timeModified,
|
|
178
|
-
post_count: d.postCount,
|
|
179
|
-
unread: d.unread,
|
|
180
|
-
message: (0, utils_js_1.stripHtmlTags)(d.message || ""),
|
|
181
|
-
}));
|
|
182
|
-
}
|
|
70
|
+
const discussions = await (0, moodle_js_1.getForumDiscussionsApi)(apiContext.session, resolved.forumId);
|
|
71
|
+
const items = discussions.map(d => ({
|
|
72
|
+
id: d.id,
|
|
73
|
+
name: d.name,
|
|
74
|
+
user_id: d.userId,
|
|
75
|
+
time_modified: d.timeModified,
|
|
76
|
+
post_count: d.postCount,
|
|
77
|
+
unread: d.unread,
|
|
78
|
+
message: (0, utils_js_1.stripHtmlTags)(d.message || ""),
|
|
79
|
+
}));
|
|
80
|
+
(0, index_js_1.formatAndOutput)(items, output, apiContext.log, { status: "success", timestamp: new Date().toISOString(), forum_id: resolved.forumId, forum_name: resolved.name ?? null, course_id: resolved.courseid ?? null, total_discussions: discussions.length });
|
|
183
81
|
});
|
|
184
82
|
forumsCmd
|
|
185
83
|
.command("posts")
|
|
@@ -188,44 +86,23 @@ function registerForumsCommand(program) {
|
|
|
188
86
|
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
189
87
|
.action(async (discussionId, options, command) => {
|
|
190
88
|
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
191
|
-
const apiContext = await createApiContext(options, command);
|
|
89
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
192
90
|
if (!apiContext) {
|
|
193
91
|
process.exitCode = 1;
|
|
194
92
|
return;
|
|
195
93
|
}
|
|
196
94
|
const posts = await (0, moodle_js_1.getDiscussionPostsApi)(apiContext.session, parseInt(discussionId, 10));
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
modified: (0, utils_js_1.formatTimestamp)(p.modified),
|
|
209
|
-
message: p.message,
|
|
210
|
-
unread: p.unread,
|
|
211
|
-
})),
|
|
212
|
-
summary: {
|
|
213
|
-
total_posts: posts.length,
|
|
214
|
-
},
|
|
215
|
-
};
|
|
216
|
-
console.log(JSON.stringify(result));
|
|
217
|
-
}
|
|
218
|
-
else if (output === "table") {
|
|
219
|
-
console.log(`Discussion ${discussionId} - ${posts.length} posts`);
|
|
220
|
-
console.log("Use --output json to see full post content");
|
|
221
|
-
const tablePosts = posts.map(p => ({
|
|
222
|
-
id: p.id,
|
|
223
|
-
subject: p.subject.substring(0, 50) + (p.subject.length > 50 ? "..." : ""),
|
|
224
|
-
author: p.author,
|
|
225
|
-
created: new Date(p.created * 1000).toLocaleString(),
|
|
226
|
-
}));
|
|
227
|
-
console.table(tablePosts);
|
|
228
|
-
}
|
|
95
|
+
const items = posts.map(p => ({
|
|
96
|
+
id: p.id,
|
|
97
|
+
subject: p.subject,
|
|
98
|
+
author: p.author,
|
|
99
|
+
author_id: p.authorId,
|
|
100
|
+
created: (0, utils_js_1.formatTimestamp)(p.created),
|
|
101
|
+
modified: (0, utils_js_1.formatTimestamp)(p.modified),
|
|
102
|
+
message: p.message,
|
|
103
|
+
unread: p.unread,
|
|
104
|
+
}));
|
|
105
|
+
(0, index_js_1.formatAndOutput)(items, output, apiContext.log, { status: "success", timestamp: new Date().toISOString(), discussion_id: discussionId, total_posts: posts.length });
|
|
229
106
|
});
|
|
230
107
|
forumsCmd
|
|
231
108
|
.command("post")
|
|
@@ -236,28 +113,20 @@ function registerForumsCommand(program) {
|
|
|
236
113
|
.option("--subscribe", "Subscribe to the discussion", false)
|
|
237
114
|
.option("--pin", "Pin the discussion", false)
|
|
238
115
|
.action(async (forumId, subject, message, options, command) => {
|
|
239
|
-
const apiContext = await createApiContext(options, command);
|
|
116
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
240
117
|
if (!apiContext) {
|
|
241
118
|
process.exitCode = 1;
|
|
242
119
|
return;
|
|
243
120
|
}
|
|
244
121
|
const { log, session } = apiContext;
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
classification: "inprogress",
|
|
248
|
-
});
|
|
249
|
-
const courseIds = courses.map(c => c.id);
|
|
250
|
-
const wsForums = await (0, moodle_js_1.getForumsApi)(session, courseIds);
|
|
251
|
-
// Find forum by cmid or instance ID
|
|
252
|
-
const targetForum = wsForums.find(f => f.cmid.toString() === forumId || f.id === parseInt(forumId, 10));
|
|
253
|
-
if (!targetForum) {
|
|
122
|
+
const resolved = await (0, moodle_js_1.resolveForumId)(session, forumId);
|
|
123
|
+
if (!resolved) {
|
|
254
124
|
log.error(`Forum not found: ${forumId}`);
|
|
255
125
|
process.exitCode = 1;
|
|
256
126
|
return;
|
|
257
127
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
const result = await (0, moodle_js_1.addForumDiscussionApi)(session, targetForum.id, subject, message);
|
|
128
|
+
log.info(`Posting to forum: ${resolved.name ?? forumId}`);
|
|
129
|
+
const result = await (0, moodle_js_1.addForumDiscussionApi)(session, resolved.forumId, subject, message);
|
|
261
130
|
if (result.success) {
|
|
262
131
|
log.success(`✓ Discussion posted successfully!`);
|
|
263
132
|
log.info(` Discussion ID: ${result.discussionId}`);
|
|
@@ -276,7 +145,7 @@ function registerForumsCommand(program) {
|
|
|
276
145
|
.option("--attachment-id <id>", "Draft file ID for attachment")
|
|
277
146
|
.option("--inline-attachment-id <id>", "Draft file ID for inline attachment")
|
|
278
147
|
.action(async (postId, subject, message, options, command) => {
|
|
279
|
-
const apiContext = await createApiContext(options, command);
|
|
148
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
280
149
|
if (!apiContext) {
|
|
281
150
|
process.exitCode = 1;
|
|
282
151
|
return;
|
|
@@ -306,7 +175,7 @@ function registerForumsCommand(program) {
|
|
|
306
175
|
.description("Delete a forum post or discussion (by post ID)")
|
|
307
176
|
.argument("<post-id>", "Post ID to delete (deletes entire discussion if it's the first post)")
|
|
308
177
|
.action(async (postId, options, command) => {
|
|
309
|
-
const apiContext = await createApiContext(options, command);
|
|
178
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
310
179
|
if (!apiContext) {
|
|
311
180
|
process.exitCode = 1;
|
|
312
181
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grades.d.ts","sourceRoot":"","sources":["../../../src/src/commands/grades.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"grades.d.ts","sourceRoot":"","sources":["../../../src/src/commands/grades.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBpC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA+F5D"}
|
|
@@ -1,66 +1,32 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.registerGradesCommand = registerGradesCommand;
|
|
7
|
-
const utils_js_1 = require("../lib/utils.js");
|
|
8
4
|
const moodle_js_1 = require("../lib/moodle.js");
|
|
9
|
-
const
|
|
10
|
-
const token_js_1 = require("../lib/token.js");
|
|
5
|
+
const auth_js_1 = require("../lib/auth.js");
|
|
11
6
|
const index_js_1 = require("../index.js");
|
|
12
|
-
const
|
|
13
|
-
const node_fs_1 = __importDefault(require("node:fs"));
|
|
7
|
+
const utils_js_1 = require("../lib/utils.js");
|
|
14
8
|
function registerGradesCommand(program) {
|
|
15
9
|
const gradesCmd = program.command("grades");
|
|
16
10
|
gradesCmd.description("Grade operations");
|
|
17
|
-
function getOutputFormat(command) {
|
|
18
|
-
const opts = command.optsWithGlobals();
|
|
19
|
-
return opts.output || "json";
|
|
20
|
-
}
|
|
21
|
-
// Pure API context - no browser required (fast!)
|
|
22
|
-
async function createApiContext(options, command) {
|
|
23
|
-
const opts = command?.optsWithGlobals ? command.optsWithGlobals() : options;
|
|
24
|
-
const outputFormat = getOutputFormat(command || { optsWithGlobals: () => ({ output: "json" }) });
|
|
25
|
-
const silent = outputFormat === "json" && !opts.verbose;
|
|
26
|
-
const log = (0, logger_js_1.createLogger)(opts.verbose, silent);
|
|
27
|
-
const baseDir = (0, utils_js_1.getBaseDir)();
|
|
28
|
-
const sessionPath = node_path_1.default.resolve(baseDir, ".auth", "storage-state.json");
|
|
29
|
-
// Check if session exists
|
|
30
|
-
if (!node_fs_1.default.existsSync(sessionPath)) {
|
|
31
|
-
log.error("未找到登入 session。請先執行 'openape auth login' 進行登入。");
|
|
32
|
-
log.info(`Session 預期位置: ${sessionPath}`);
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
// Try to load WS token
|
|
36
|
-
const wsToken = (0, token_js_1.loadWsToken)(sessionPath);
|
|
37
|
-
if (!wsToken) {
|
|
38
|
-
log.error("未找到 WS token。請先執行 'openape auth login' 進行登入。");
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
return {
|
|
42
|
-
log,
|
|
43
|
-
session: {
|
|
44
|
-
wsToken,
|
|
45
|
-
moodleBaseUrl: "https://ilearning.cycu.edu.tw",
|
|
46
|
-
},
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
11
|
gradesCmd
|
|
50
12
|
.command("summary")
|
|
51
13
|
.description("Show grade summary across all courses")
|
|
52
14
|
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
53
15
|
.action(async (options, command) => {
|
|
54
|
-
const output = getOutputFormat(command);
|
|
55
|
-
const apiContext = await createApiContext(options, command);
|
|
16
|
+
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
17
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
56
18
|
if (!apiContext) {
|
|
57
19
|
process.exitCode = 1;
|
|
58
20
|
return;
|
|
59
21
|
}
|
|
60
22
|
const courses = await (0, moodle_js_1.getEnrolledCoursesApi)(apiContext.session);
|
|
23
|
+
const gradeResults = await Promise.allSettled(courses.map(course => (0, moodle_js_1.getCourseGradesApi)(apiContext.session, course.id)
|
|
24
|
+
.then(grades => ({ course, grades }))));
|
|
61
25
|
const gradeSummaries = [];
|
|
62
|
-
for (const
|
|
63
|
-
|
|
26
|
+
for (const result of gradeResults) {
|
|
27
|
+
if (result.status !== "fulfilled")
|
|
28
|
+
continue;
|
|
29
|
+
const { course, grades } = result.value;
|
|
64
30
|
gradeSummaries.push({
|
|
65
31
|
courseId: course.id,
|
|
66
32
|
courseName: course.fullname,
|
|
@@ -70,19 +36,13 @@ function registerGradesCommand(program) {
|
|
|
70
36
|
totalUsers: grades.totalUsers,
|
|
71
37
|
});
|
|
72
38
|
}
|
|
73
|
-
// Calculate overall statistics
|
|
74
39
|
const gradedCourses = gradeSummaries.filter(g => g.grade !== undefined && g.grade !== null && g.grade !== "-");
|
|
75
40
|
const averageRank = gradeSummaries
|
|
76
41
|
.filter(g => g.rank !== undefined && g.rank !== null)
|
|
77
42
|
.reduce((sum, g) => sum + (g.rank || 0), 0) /
|
|
78
43
|
(gradeSummaries.filter(g => g.rank !== undefined && g.rank !== null).length || 1);
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
graded_courses: gradedCourses.length,
|
|
82
|
-
average_rank: averageRank.toFixed(1),
|
|
83
|
-
grades: gradeSummaries,
|
|
84
|
-
};
|
|
85
|
-
(0, index_js_1.formatAndOutput)(summaryData, output, apiContext.log);
|
|
44
|
+
apiContext.log.info(`Total: ${courses.length} courses, ${gradedCourses.length} graded, avg rank: ${averageRank.toFixed(1)}`);
|
|
45
|
+
(0, index_js_1.formatAndOutput)(gradeSummaries, output, apiContext.log);
|
|
86
46
|
});
|
|
87
47
|
gradesCmd
|
|
88
48
|
.command("course")
|
|
@@ -90,8 +50,8 @@ function registerGradesCommand(program) {
|
|
|
90
50
|
.argument("<course-id>", "Course ID")
|
|
91
51
|
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
92
52
|
.action(async (courseId, options, command) => {
|
|
93
|
-
const output = getOutputFormat(command);
|
|
94
|
-
const apiContext = await createApiContext(options, command);
|
|
53
|
+
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
54
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
95
55
|
if (!apiContext) {
|
|
96
56
|
process.exitCode = 1;
|
|
97
57
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"materials.d.ts","sourceRoot":"","sources":["../../../src/src/commands/materials.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"materials.d.ts","sourceRoot":"","sources":["../../../src/src/commands/materials.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4BpC,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAwd/D"}
|