@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.
Files changed (81) hide show
  1. package/README.md +11 -8
  2. package/esm/deno.js +1 -1
  3. package/esm/src/commands/announcements.d.ts.map +1 -1
  4. package/esm/src/commands/announcements.js +22 -85
  5. package/esm/src/commands/assignments.d.ts.map +1 -1
  6. package/esm/src/commands/assignments.js +2 -3
  7. package/esm/src/commands/auth.d.ts.map +1 -1
  8. package/esm/src/commands/auth.js +24 -14
  9. package/esm/src/commands/calendar.d.ts.map +1 -1
  10. package/esm/src/commands/calendar.js +32 -84
  11. package/esm/src/commands/courses.d.ts.map +1 -1
  12. package/esm/src/commands/courses.js +2 -38
  13. package/esm/src/commands/forums.d.ts.map +1 -1
  14. package/esm/src/commands/forums.js +47 -175
  15. package/esm/src/commands/grades.d.ts.map +1 -1
  16. package/esm/src/commands/grades.js +10 -47
  17. package/esm/src/commands/materials.d.ts.map +1 -1
  18. package/esm/src/commands/materials.js +135 -223
  19. package/esm/src/commands/quizzes.d.ts.map +1 -1
  20. package/esm/src/commands/quizzes.js +32 -56
  21. package/esm/src/commands/skills.js +3 -3
  22. package/esm/src/commands/upload.d.ts.map +1 -1
  23. package/esm/src/commands/upload.js +2 -5
  24. package/esm/src/commands/videos.d.ts.map +1 -1
  25. package/esm/src/commands/videos.js +6 -76
  26. package/esm/src/index.d.ts +2 -1
  27. package/esm/src/index.d.ts.map +1 -1
  28. package/esm/src/index.js +5 -1
  29. package/esm/src/lib/auth.d.ts +21 -2
  30. package/esm/src/lib/auth.d.ts.map +1 -1
  31. package/esm/src/lib/auth.js +79 -20
  32. package/esm/src/lib/logger.d.ts +2 -2
  33. package/esm/src/lib/logger.d.ts.map +1 -1
  34. package/esm/src/lib/logger.js +6 -4
  35. package/esm/src/lib/moodle.d.ts +18 -0
  36. package/esm/src/lib/moodle.d.ts.map +1 -1
  37. package/esm/src/lib/moodle.js +54 -2
  38. package/esm/src/lib/utils.d.ts +3 -8
  39. package/esm/src/lib/utils.d.ts.map +1 -1
  40. package/esm/src/lib/utils.js +3 -10
  41. package/package.json +1 -1
  42. package/script/deno.js +1 -1
  43. package/script/src/commands/announcements.d.ts.map +1 -1
  44. package/script/src/commands/announcements.js +23 -89
  45. package/script/src/commands/assignments.d.ts.map +1 -1
  46. package/script/src/commands/assignments.js +2 -3
  47. package/script/src/commands/auth.d.ts.map +1 -1
  48. package/script/src/commands/auth.js +24 -14
  49. package/script/src/commands/calendar.d.ts.map +1 -1
  50. package/script/src/commands/calendar.js +33 -85
  51. package/script/src/commands/courses.d.ts.map +1 -1
  52. package/script/src/commands/courses.js +9 -48
  53. package/script/src/commands/forums.d.ts.map +1 -1
  54. package/script/src/commands/forums.js +50 -181
  55. package/script/src/commands/grades.d.ts.map +1 -1
  56. package/script/src/commands/grades.js +14 -54
  57. package/script/src/commands/materials.d.ts.map +1 -1
  58. package/script/src/commands/materials.js +132 -220
  59. package/script/src/commands/quizzes.d.ts.map +1 -1
  60. package/script/src/commands/quizzes.js +40 -67
  61. package/script/src/commands/skills.js +3 -3
  62. package/script/src/commands/upload.d.ts.map +1 -1
  63. package/script/src/commands/upload.js +2 -5
  64. package/script/src/commands/videos.d.ts.map +1 -1
  65. package/script/src/commands/videos.js +11 -81
  66. package/script/src/index.d.ts +2 -1
  67. package/script/src/index.d.ts.map +1 -1
  68. package/script/src/index.js +5 -1
  69. package/script/src/lib/auth.d.ts +21 -2
  70. package/script/src/lib/auth.d.ts.map +1 -1
  71. package/script/src/lib/auth.js +83 -56
  72. package/script/src/lib/logger.d.ts +2 -2
  73. package/script/src/lib/logger.d.ts.map +1 -1
  74. package/script/src/lib/logger.js +6 -4
  75. package/script/src/lib/moodle.d.ts +18 -0
  76. package/script/src/lib/moodle.d.ts.map +1 -1
  77. package/script/src/lib/moodle.js +56 -2
  78. package/script/src/lib/utils.d.ts +3 -8
  79. package/script/src/lib/utils.d.ts.map +1 -1
  80. package/script/src/lib/utils.js +3 -11
  81. 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 logger_js_1 = require("../lib/logger.js");
10
- const token_js_1 = require("../lib/token.js");
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
- // Pure API context - no browser required (fast!)
17
- async function createApiContext(options, command) {
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: "inprogress",
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 = courses.find(c => c.id === wsForum.courseid);
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
- console.log(JSON.stringify({
80
- status: "success",
81
- timestamp: new Date().toISOString(),
82
- total_courses: courses.length,
83
- total_forums: allForums.length,
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
- .option("--output <format>", "Output format: json|csv|table|silent")
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
- const courses = await (0, moodle_js_1.getEnrolledCoursesApi)(apiContext.session, {
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 apiContext = await createApiContext(options, command);
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
- // Get courses via WS API
144
- const courses = await (0, moodle_js_1.getEnrolledCoursesApi)(apiContext.session, {
145
- classification: "inprogress",
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 course = courses.find(c => c.id === targetForum.courseid);
158
- // Get discussions via WS API
159
- const discussions = await (0, moodle_js_1.getForumDiscussionsApi)(apiContext.session, targetForum.id);
160
- // Output NDJSON: one line per discussion entry for stream-friendly parsing
161
- const meta = {
162
- status: "success",
163
- timestamp: new Date().toISOString(),
164
- forum_id: targetForum.id,
165
- forum_name: targetForum.name,
166
- forum_intro: targetForum.intro,
167
- course_id: course?.id,
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
- if (output === "json") {
198
- const result = {
199
- status: "success",
200
- timestamp: new Date().toISOString(),
201
- discussion_id: discussionId,
202
- posts: posts.map(p => ({
203
- id: p.id,
204
- subject: p.subject,
205
- author: p.author,
206
- author_id: p.authorId,
207
- created: (0, utils_js_1.formatTimestamp)(p.created),
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
- // Get courses to find the forum
246
- const courses = await (0, moodle_js_1.getEnrolledCoursesApi)(session, {
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
- const course = courses.find(c => c.id === targetForum.courseid);
259
- log.info(`Posting to forum: ${targetForum.name} (${course?.fullname})`);
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":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkBpC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAsI5D"}
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 logger_js_1 = require("../lib/logger.js");
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 node_path_1 = __importDefault(require("node:path"));
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 course of courses) {
63
- const grades = await (0, moodle_js_1.getCourseGradesApi)(apiContext.session, course.id);
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
- const summaryData = {
80
- total_courses: courses.length,
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;AAgCpC,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA8iB/D"}
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"}