@mo7yw4ng/openape 1.0.6 → 2.0.3
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/bin/openape +29 -0
- package/bin/openape.js +29 -0
- package/package.json +22 -28
- package/LICENSE +0 -21
- package/README.md +0 -138
- package/esm/_dnt.polyfills.d.ts +0 -101
- package/esm/_dnt.polyfills.d.ts.map +0 -1
- package/esm/_dnt.polyfills.js +0 -127
- package/esm/_dnt.shims.d.ts +0 -6
- package/esm/_dnt.shims.d.ts.map +0 -1
- package/esm/_dnt.shims.js +0 -61
- package/esm/deno.d.ts +0 -25
- package/esm/deno.d.ts.map +0 -1
- package/esm/deno.js +0 -23
- package/esm/package.json +0 -3
- package/esm/src/commands/announcements.d.ts +0 -3
- package/esm/src/commands/announcements.d.ts.map +0 -1
- package/esm/src/commands/announcements.js +0 -71
- package/esm/src/commands/assignments.d.ts +0 -3
- package/esm/src/commands/assignments.d.ts.map +0 -1
- package/esm/src/commands/assignments.js +0 -229
- package/esm/src/commands/auth.d.ts +0 -3
- package/esm/src/commands/auth.d.ts.map +0 -1
- package/esm/src/commands/auth.js +0 -290
- package/esm/src/commands/calendar.d.ts +0 -3
- package/esm/src/commands/calendar.d.ts.map +0 -1
- package/esm/src/commands/calendar.js +0 -127
- package/esm/src/commands/courses.d.ts +0 -3
- package/esm/src/commands/courses.d.ts.map +0 -1
- package/esm/src/commands/courses.js +0 -312
- package/esm/src/commands/forums.d.ts +0 -3
- package/esm/src/commands/forums.d.ts.map +0 -1
- package/esm/src/commands/forums.js +0 -190
- package/esm/src/commands/grades.d.ts +0 -3
- package/esm/src/commands/grades.d.ts.map +0 -1
- package/esm/src/commands/grades.js +0 -84
- package/esm/src/commands/materials.d.ts +0 -3
- package/esm/src/commands/materials.d.ts.map +0 -1
- package/esm/src/commands/materials.js +0 -402
- package/esm/src/commands/quizzes.d.ts +0 -3
- package/esm/src/commands/quizzes.d.ts.map +0 -1
- package/esm/src/commands/quizzes.js +0 -236
- package/esm/src/commands/skills.d.ts +0 -3
- package/esm/src/commands/skills.d.ts.map +0 -1
- package/esm/src/commands/skills.js +0 -106
- package/esm/src/commands/upload.d.ts +0 -3
- package/esm/src/commands/upload.d.ts.map +0 -1
- package/esm/src/commands/upload.js +0 -55
- package/esm/src/commands/videos.d.ts +0 -3
- package/esm/src/commands/videos.d.ts.map +0 -1
- package/esm/src/commands/videos.js +0 -266
- package/esm/src/index.d.ts +0 -28
- package/esm/src/index.d.ts.map +0 -1
- package/esm/src/index.js +0 -164
- package/esm/src/lib/auth.d.ts +0 -66
- package/esm/src/lib/auth.d.ts.map +0 -1
- package/esm/src/lib/auth.js +0 -286
- package/esm/src/lib/config.d.ts +0 -6
- package/esm/src/lib/config.d.ts.map +0 -1
- package/esm/src/lib/config.js +0 -36
- package/esm/src/lib/logger.d.ts +0 -3
- package/esm/src/lib/logger.d.ts.map +0 -1
- package/esm/src/lib/logger.js +0 -26
- package/esm/src/lib/moodle.d.ts +0 -447
- package/esm/src/lib/moodle.d.ts.map +0 -1
- package/esm/src/lib/moodle.js +0 -1353
- package/esm/src/lib/session.d.ts +0 -8
- package/esm/src/lib/session.d.ts.map +0 -1
- package/esm/src/lib/session.js +0 -42
- package/esm/src/lib/token.d.ts +0 -38
- package/esm/src/lib/token.d.ts.map +0 -1
- package/esm/src/lib/token.js +0 -178
- package/esm/src/lib/types.d.ts +0 -189
- package/esm/src/lib/types.d.ts.map +0 -1
- package/esm/src/lib/types.js +0 -2
- package/esm/src/lib/utils.d.ts +0 -52
- package/esm/src/lib/utils.d.ts.map +0 -1
- package/esm/src/lib/utils.js +0 -122
- package/script/_dnt.polyfills.d.ts +0 -101
- package/script/_dnt.polyfills.d.ts.map +0 -1
- package/script/_dnt.polyfills.js +0 -130
- package/script/_dnt.shims.d.ts +0 -6
- package/script/_dnt.shims.d.ts.map +0 -1
- package/script/_dnt.shims.js +0 -65
- package/script/deno.d.ts +0 -25
- package/script/deno.d.ts.map +0 -1
- package/script/deno.js +0 -25
- package/script/package.json +0 -3
- package/script/src/commands/announcements.d.ts +0 -3
- package/script/src/commands/announcements.d.ts.map +0 -1
- package/script/src/commands/announcements.js +0 -74
- package/script/src/commands/assignments.d.ts +0 -3
- package/script/src/commands/assignments.d.ts.map +0 -1
- package/script/src/commands/assignments.js +0 -268
- package/script/src/commands/auth.d.ts +0 -3
- package/script/src/commands/auth.d.ts.map +0 -1
- package/script/src/commands/auth.js +0 -296
- package/script/src/commands/calendar.d.ts +0 -3
- package/script/src/commands/calendar.d.ts.map +0 -1
- package/script/src/commands/calendar.js +0 -133
- package/script/src/commands/courses.d.ts +0 -3
- package/script/src/commands/courses.d.ts.map +0 -1
- package/script/src/commands/courses.js +0 -315
- package/script/src/commands/forums.d.ts +0 -3
- package/script/src/commands/forums.d.ts.map +0 -1
- package/script/src/commands/forums.js +0 -193
- package/script/src/commands/grades.d.ts +0 -3
- package/script/src/commands/grades.d.ts.map +0 -1
- package/script/src/commands/grades.js +0 -87
- package/script/src/commands/materials.d.ts +0 -3
- package/script/src/commands/materials.d.ts.map +0 -1
- package/script/src/commands/materials.js +0 -408
- package/script/src/commands/quizzes.d.ts +0 -3
- package/script/src/commands/quizzes.d.ts.map +0 -1
- package/script/src/commands/quizzes.js +0 -239
- package/script/src/commands/skills.d.ts +0 -3
- package/script/src/commands/skills.d.ts.map +0 -1
- package/script/src/commands/skills.js +0 -112
- package/script/src/commands/upload.d.ts +0 -3
- package/script/src/commands/upload.d.ts.map +0 -1
- package/script/src/commands/upload.js +0 -61
- package/script/src/commands/videos.d.ts +0 -3
- package/script/src/commands/videos.d.ts.map +0 -1
- package/script/src/commands/videos.js +0 -272
- package/script/src/index.d.ts +0 -28
- package/script/src/index.d.ts.map +0 -1
- package/script/src/index.js +0 -171
- package/script/src/lib/auth.d.ts +0 -66
- package/script/src/lib/auth.d.ts.map +0 -1
- package/script/src/lib/auth.js +0 -296
- package/script/src/lib/config.d.ts +0 -6
- package/script/src/lib/config.d.ts.map +0 -1
- package/script/src/lib/config.js +0 -42
- package/script/src/lib/logger.d.ts +0 -3
- package/script/src/lib/logger.d.ts.map +0 -1
- package/script/src/lib/logger.js +0 -29
- package/script/src/lib/moodle.d.ts +0 -447
- package/script/src/lib/moodle.d.ts.map +0 -1
- package/script/src/lib/moodle.js +0 -1425
- package/script/src/lib/session.d.ts +0 -8
- package/script/src/lib/session.d.ts.map +0 -1
- package/script/src/lib/session.js +0 -45
- package/script/src/lib/token.d.ts +0 -38
- package/script/src/lib/token.d.ts.map +0 -1
- package/script/src/lib/token.js +0 -189
- package/script/src/lib/types.d.ts +0 -189
- package/script/src/lib/types.d.ts.map +0 -1
- package/script/src/lib/types.js +0 -3
- package/script/src/lib/utils.d.ts +0 -52
- package/script/src/lib/utils.d.ts.map +0 -1
- package/script/src/lib/utils.js +0 -167
- package/skills/openape/SKILL.md +0 -115
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.registerGradesCommand = registerGradesCommand;
|
|
4
|
-
const moodle_js_1 = require("../lib/moodle.js");
|
|
5
|
-
const auth_js_1 = require("../lib/auth.js");
|
|
6
|
-
const index_js_1 = require("../index.js");
|
|
7
|
-
const utils_js_1 = require("../lib/utils.js");
|
|
8
|
-
function registerGradesCommand(program) {
|
|
9
|
-
const gradesCmd = program.command("grades");
|
|
10
|
-
gradesCmd.description("Grade operations");
|
|
11
|
-
gradesCmd
|
|
12
|
-
.command("summary")
|
|
13
|
-
.description("Show grade summary across all courses")
|
|
14
|
-
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
15
|
-
.action(async (options, command) => {
|
|
16
|
-
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
17
|
-
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
18
|
-
if (!apiContext) {
|
|
19
|
-
process.exitCode = 1;
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
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 }))));
|
|
25
|
-
const gradeSummaries = [];
|
|
26
|
-
for (const result of gradeResults) {
|
|
27
|
-
if (result.status !== "fulfilled")
|
|
28
|
-
continue;
|
|
29
|
-
const { course, grades } = result.value;
|
|
30
|
-
gradeSummaries.push({
|
|
31
|
-
courseId: course.id,
|
|
32
|
-
courseName: course.fullname,
|
|
33
|
-
grade: grades.grade,
|
|
34
|
-
gradeFormatted: grades.gradeFormatted,
|
|
35
|
-
rank: grades.rank,
|
|
36
|
-
totalUsers: grades.totalUsers,
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
const gradedCourses = gradeSummaries.filter(g => g.grade !== undefined && g.grade !== null && g.grade !== "-");
|
|
40
|
-
const averageRank = gradeSummaries
|
|
41
|
-
.filter(g => g.rank !== undefined && g.rank !== null)
|
|
42
|
-
.reduce((sum, g) => sum + (g.rank || 0), 0) /
|
|
43
|
-
(gradeSummaries.filter(g => g.rank !== undefined && g.rank !== null).length || 1);
|
|
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);
|
|
46
|
-
});
|
|
47
|
-
gradesCmd
|
|
48
|
-
.command("course")
|
|
49
|
-
.description("Show detailed grades for a specific course")
|
|
50
|
-
.argument("<course-id>", "Course ID")
|
|
51
|
-
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
52
|
-
.action(async (courseId, options, command) => {
|
|
53
|
-
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
54
|
-
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
55
|
-
if (!apiContext) {
|
|
56
|
-
process.exitCode = 1;
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
const courses = await (0, moodle_js_1.getEnrolledCoursesApi)(apiContext.session);
|
|
60
|
-
const course = courses.find(c => c.id === parseInt(courseId, 10));
|
|
61
|
-
if (!course) {
|
|
62
|
-
apiContext.log.error(`Course not found: ${courseId}`);
|
|
63
|
-
process.exitCode = 1;
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
const grades = await (0, moodle_js_1.getCourseGradesApi)(apiContext.session, course.id);
|
|
67
|
-
const gradeData = {
|
|
68
|
-
courseId: grades.courseId,
|
|
69
|
-
courseName: grades.courseName,
|
|
70
|
-
grade: grades.grade,
|
|
71
|
-
gradeFormatted: grades.gradeFormatted,
|
|
72
|
-
rank: grades.rank,
|
|
73
|
-
totalUsers: grades.totalUsers,
|
|
74
|
-
items: grades.items?.map(item => ({
|
|
75
|
-
name: item.name,
|
|
76
|
-
grade: item.grade,
|
|
77
|
-
gradeFormatted: item.gradeFormatted,
|
|
78
|
-
range: item.range,
|
|
79
|
-
percentage: item.percentage,
|
|
80
|
-
weight: item.weight,
|
|
81
|
-
feedback: item.feedback,
|
|
82
|
-
graded: item.graded,
|
|
83
|
-
})),
|
|
84
|
-
};
|
|
85
|
-
(0, index_js_1.formatAndOutput)(gradeData, output, apiContext.log);
|
|
86
|
-
});
|
|
87
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
|
@@ -1,408 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.registerMaterialsCommand = registerMaterialsCommand;
|
|
7
|
-
const utils_js_1 = require("../lib/utils.js");
|
|
8
|
-
const moodle_js_1 = require("../lib/moodle.js");
|
|
9
|
-
const auth_js_1 = require("../lib/auth.js");
|
|
10
|
-
const index_js_1 = require("../index.js");
|
|
11
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
12
|
-
const node_fs_1 = __importDefault(require("node:fs"));
|
|
13
|
-
function registerMaterialsCommand(program) {
|
|
14
|
-
const materialsCmd = program.command("materials");
|
|
15
|
-
materialsCmd.description("Material/resource operations");
|
|
16
|
-
// Helper to download a single resource via HTTP (no browser needed)
|
|
17
|
-
async function downloadResourceHttp(resource, outputDir, log, token) {
|
|
18
|
-
try {
|
|
19
|
-
// Only download resource type (skip url)
|
|
20
|
-
if (resource.modType !== "resource") {
|
|
21
|
-
log.debug(` Skipping ${resource.modType}: ${resource.name}`);
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
const courseDir = node_path_1.default.join(outputDir, (0, utils_js_1.sanitizeFilename)(resource.course_name));
|
|
25
|
-
await node_fs_1.default.promises.mkdir(courseDir, { recursive: true });
|
|
26
|
-
// Build filename
|
|
27
|
-
let filename = (0, utils_js_1.sanitizeFilename)(resource.name);
|
|
28
|
-
if (resource.mimetype && !node_path_1.default.extname(filename)) {
|
|
29
|
-
const extMap = {
|
|
30
|
-
"application/pdf": ".pdf",
|
|
31
|
-
"application/vnd.ms-powerpoint": ".ppt",
|
|
32
|
-
"application/vnd.openxmlformats-officedocument.presentationml.presentation": ".pptx",
|
|
33
|
-
"application/msword": ".doc",
|
|
34
|
-
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": ".docx",
|
|
35
|
-
"application/vnd.ms-excel": ".xls",
|
|
36
|
-
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": ".xlsx",
|
|
37
|
-
"application/zip": ".zip",
|
|
38
|
-
"image/jpeg": ".jpg",
|
|
39
|
-
"image/png": ".png",
|
|
40
|
-
};
|
|
41
|
-
if (extMap[resource.mimetype]) {
|
|
42
|
-
filename += extMap[resource.mimetype];
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
const outputPath = node_path_1.default.join(courseDir, filename);
|
|
46
|
-
// Skip if already exists
|
|
47
|
-
if (node_fs_1.default.existsSync(outputPath)) {
|
|
48
|
-
log.debug(` Skipping (exists): ${filename}`);
|
|
49
|
-
const stats = await node_fs_1.default.promises.stat(outputPath);
|
|
50
|
-
return { filename, path: outputPath, size: stats.size, course_id: resource.course_id, course_name: resource.course_name };
|
|
51
|
-
}
|
|
52
|
-
// Download via HTTP with WS token
|
|
53
|
-
const separator = resource.url.includes("?") ? "&" : "?";
|
|
54
|
-
const downloadUrl = `${resource.url}${separator}token=${token}`;
|
|
55
|
-
log.debug(` Downloading: ${resource.name}`);
|
|
56
|
-
const response = await fetch(downloadUrl);
|
|
57
|
-
if (!response.ok) {
|
|
58
|
-
log.warn(` Failed to download ${resource.name}: HTTP ${response.status}`);
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
62
|
-
const data = new Uint8Array(arrayBuffer);
|
|
63
|
-
await node_fs_1.default.promises.writeFile(outputPath, data);
|
|
64
|
-
log.success(` Downloaded: ${filename} (${(0, utils_js_1.formatFileSize)(data.byteLength, 1)} KB)`);
|
|
65
|
-
return { filename, path: outputPath, size: data.byteLength, course_id: resource.course_id, course_name: resource.course_name };
|
|
66
|
-
}
|
|
67
|
-
catch (err) {
|
|
68
|
-
log.warn(` Failed to download ${resource.name}: ${err instanceof Error ? err.message : String(err)}`);
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
// Helper to build material list from API resources
|
|
73
|
-
function buildMaterialsList(courses, apiResources) {
|
|
74
|
-
const courseMap = new Map(courses.map(c => [c.id, c]));
|
|
75
|
-
const materials = [];
|
|
76
|
-
for (const resource of apiResources) {
|
|
77
|
-
const course = courseMap.get(resource.courseId);
|
|
78
|
-
if (course) {
|
|
79
|
-
materials.push({
|
|
80
|
-
course_id: resource.courseId,
|
|
81
|
-
course_name: course.fullname,
|
|
82
|
-
cmid: resource.cmid,
|
|
83
|
-
name: resource.name,
|
|
84
|
-
url: resource.url,
|
|
85
|
-
modType: resource.modType,
|
|
86
|
-
mimetype: resource.mimetype,
|
|
87
|
-
filesize: resource.filesize,
|
|
88
|
-
modified: resource.modified,
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return materials;
|
|
93
|
-
}
|
|
94
|
-
materialsCmd
|
|
95
|
-
.command("list-all")
|
|
96
|
-
.description("List all materials/resources across all courses")
|
|
97
|
-
.option("--level <type>", "Course level: in_progress (default) | all", "in_progress")
|
|
98
|
-
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
99
|
-
.action(async (options, command) => {
|
|
100
|
-
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
101
|
-
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
102
|
-
if (!apiContext) {
|
|
103
|
-
process.exitCode = 1;
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
const classification = options.level === "all" ? undefined : "inprogress";
|
|
107
|
-
const courses = await (0, moodle_js_1.getEnrolledCoursesApi)(apiContext.session, {
|
|
108
|
-
classification,
|
|
109
|
-
});
|
|
110
|
-
const courseIds = courses.map(c => c.id);
|
|
111
|
-
const apiResources = await (0, moodle_js_1.getResourcesByCoursesApi)(apiContext.session, courseIds);
|
|
112
|
-
const courseMap = new Map(courses.map(c => [c.id, c]));
|
|
113
|
-
const allMaterials = [];
|
|
114
|
-
for (const resource of apiResources) {
|
|
115
|
-
const course = courseMap.get(resource.courseId);
|
|
116
|
-
if (course) {
|
|
117
|
-
allMaterials.push({
|
|
118
|
-
course_id: resource.courseId,
|
|
119
|
-
course_name: course.fullname,
|
|
120
|
-
cmid: resource.cmid,
|
|
121
|
-
name: resource.name,
|
|
122
|
-
url: resource.url,
|
|
123
|
-
modType: resource.modType,
|
|
124
|
-
mimetype: resource.mimetype,
|
|
125
|
-
filesize: resource.filesize,
|
|
126
|
-
modified: resource.modified,
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
const items = allMaterials.map(m => ({
|
|
131
|
-
course_id: m.course_id,
|
|
132
|
-
course_name: m.course_name,
|
|
133
|
-
id: m.cmid,
|
|
134
|
-
name: m.name,
|
|
135
|
-
type: m.modType,
|
|
136
|
-
mimetype: m.mimetype,
|
|
137
|
-
filesize: m.filesize,
|
|
138
|
-
modified: m.modified ? new Date(m.modified * 1000).toISOString() : null,
|
|
139
|
-
url: m.url,
|
|
140
|
-
}));
|
|
141
|
-
(0, index_js_1.formatAndOutput)(items, output, apiContext.log, {
|
|
142
|
-
status: "success",
|
|
143
|
-
timestamp: new Date().toISOString(),
|
|
144
|
-
total_courses: courses.length,
|
|
145
|
-
total_materials: allMaterials.length,
|
|
146
|
-
by_type: allMaterials.reduce((acc, m) => {
|
|
147
|
-
acc[m.modType] = (acc[m.modType] || 0) + 1;
|
|
148
|
-
return acc;
|
|
149
|
-
}, {}),
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
materialsCmd
|
|
153
|
-
.command("download")
|
|
154
|
-
.description("Download all materials from a specific course")
|
|
155
|
-
.argument("<course-id>", "Course ID")
|
|
156
|
-
.option("--output-dir <path>", "Output directory", "./downloads")
|
|
157
|
-
.action(async (courseId, options, command) => {
|
|
158
|
-
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
159
|
-
if (!apiContext) {
|
|
160
|
-
process.exitCode = 1;
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
const { log, session } = apiContext;
|
|
164
|
-
const courses = await (0, moodle_js_1.getEnrolledCoursesApi)(session);
|
|
165
|
-
const course = courses.find((c) => c.id === parseInt(courseId, 10));
|
|
166
|
-
if (!course) {
|
|
167
|
-
log.error(`Course not found: ${courseId}`);
|
|
168
|
-
process.exitCode = 1;
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
const apiResources = await (0, moodle_js_1.getResourcesByCoursesApi)(session, [course.id]);
|
|
172
|
-
const materials = buildMaterialsList(courses, apiResources);
|
|
173
|
-
log.info(`Found ${materials.length} materials in course: ${course.fullname}`);
|
|
174
|
-
const downloadedFiles = [];
|
|
175
|
-
for (const material of materials) {
|
|
176
|
-
const result = await downloadResourceHttp(material, options.outputDir, log, session.wsToken);
|
|
177
|
-
if (result) {
|
|
178
|
-
downloadedFiles.push(result);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
const items = downloadedFiles.map(f => ({
|
|
182
|
-
filename: f.filename,
|
|
183
|
-
path: f.path,
|
|
184
|
-
size: f.size,
|
|
185
|
-
course_id: f.course_id,
|
|
186
|
-
course_name: f.course_name,
|
|
187
|
-
}));
|
|
188
|
-
(0, index_js_1.formatAndOutput)(items, "json", log, {
|
|
189
|
-
status: "success",
|
|
190
|
-
timestamp: new Date().toISOString(),
|
|
191
|
-
total_materials: materials.length,
|
|
192
|
-
downloaded: downloadedFiles.length,
|
|
193
|
-
skipped: materials.length - downloadedFiles.length,
|
|
194
|
-
total_size: downloadedFiles.reduce((sum, f) => sum + f.size, 0),
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
materialsCmd
|
|
198
|
-
.command("download-all")
|
|
199
|
-
.description("Download all materials from all courses")
|
|
200
|
-
.option("--output-dir <path>", "Output directory", "./downloads")
|
|
201
|
-
.option("--level <type>", "Course level: in_progress (default) | all", "in_progress")
|
|
202
|
-
.action(async (options, command) => {
|
|
203
|
-
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
204
|
-
if (!apiContext) {
|
|
205
|
-
process.exitCode = 1;
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
const { log, session } = apiContext;
|
|
209
|
-
const classification = options.level === "all" ? undefined : "inprogress";
|
|
210
|
-
const courses = await (0, moodle_js_1.getEnrolledCoursesApi)(session, { classification });
|
|
211
|
-
log.info(`Scanning ${courses.length} courses for materials...`);
|
|
212
|
-
const courseIds = courses.map((c) => c.id);
|
|
213
|
-
const apiResources = await (0, moodle_js_1.getResourcesByCoursesApi)(session, courseIds);
|
|
214
|
-
const allMaterials = buildMaterialsList(courses, apiResources);
|
|
215
|
-
log.info(`Found ${allMaterials.length} materials across ${courses.length} courses`);
|
|
216
|
-
const downloadedFiles = [];
|
|
217
|
-
for (const material of allMaterials) {
|
|
218
|
-
const result = await downloadResourceHttp(material, options.outputDir, log, session.wsToken);
|
|
219
|
-
if (result) {
|
|
220
|
-
downloadedFiles.push(result);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
const items = downloadedFiles.map(f => ({
|
|
224
|
-
filename: f.filename,
|
|
225
|
-
path: f.path,
|
|
226
|
-
size: f.size,
|
|
227
|
-
course_id: f.course_id,
|
|
228
|
-
course_name: f.course_name,
|
|
229
|
-
}));
|
|
230
|
-
(0, index_js_1.formatAndOutput)(items, "json", log, {
|
|
231
|
-
status: "success",
|
|
232
|
-
timestamp: new Date().toISOString(),
|
|
233
|
-
total_courses: courses.length,
|
|
234
|
-
total_materials: allMaterials.length,
|
|
235
|
-
downloaded: downloadedFiles.length,
|
|
236
|
-
skipped: allMaterials.length - downloadedFiles.length,
|
|
237
|
-
total_size: downloadedFiles.reduce((sum, f) => sum + f.size, 0),
|
|
238
|
-
});
|
|
239
|
-
});
|
|
240
|
-
materialsCmd
|
|
241
|
-
.command("complete")
|
|
242
|
-
.description("Mark all incomplete resources (non-video) as complete in a course")
|
|
243
|
-
.argument("<course-id>", "Course ID")
|
|
244
|
-
.option("--dry-run", "Show what would be marked complete without doing it")
|
|
245
|
-
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
246
|
-
.action(async (courseId, options, command) => {
|
|
247
|
-
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
248
|
-
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
249
|
-
if (!apiContext) {
|
|
250
|
-
process.exitCode = 1;
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
try {
|
|
254
|
-
const { log, session } = apiContext;
|
|
255
|
-
// Get user ID
|
|
256
|
-
const siteInfo = await (0, moodle_js_1.getSiteInfoApi)(session);
|
|
257
|
-
// Get completion status for all activities in the course
|
|
258
|
-
const completionData = await (0, moodle_js_1.moodleApiCall)(session, "core_completion_get_activities_completion_status", { courseid: parseInt(courseId, 10), userid: siteInfo.userid });
|
|
259
|
-
if (!completionData?.statuses) {
|
|
260
|
-
log.info("No activities found in this course.");
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
// Filter for resources (non-video) that have completion enabled but are not complete
|
|
264
|
-
const incompleteResources = completionData.statuses.filter((status) => {
|
|
265
|
-
// Only resources, not supervideo
|
|
266
|
-
if (status.modname === "supervideo")
|
|
267
|
-
return false;
|
|
268
|
-
// Must have completion enabled
|
|
269
|
-
if (!status.hascompletion)
|
|
270
|
-
return false;
|
|
271
|
-
// Must be incomplete
|
|
272
|
-
if (status.isoverallcomplete)
|
|
273
|
-
return false;
|
|
274
|
-
return true;
|
|
275
|
-
});
|
|
276
|
-
if (incompleteResources.length === 0) {
|
|
277
|
-
log.info("All resources are already complete (or no resources with completion tracking).");
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
log.info(`Found ${incompleteResources.length} incomplete resources to complete:`);
|
|
281
|
-
for (const resource of incompleteResources) {
|
|
282
|
-
log.info(` - ${resource.name} (cmid: ${resource.cmid})`);
|
|
283
|
-
}
|
|
284
|
-
if (options.dryRun) {
|
|
285
|
-
log.info("\n[Dry run] No changes made.");
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
// Mark each resource as complete
|
|
289
|
-
const results = [];
|
|
290
|
-
for (const resource of incompleteResources) {
|
|
291
|
-
const success = await (0, moodle_js_1.updateActivityCompletionStatusManually)(session, resource.cmid, true);
|
|
292
|
-
results.push({
|
|
293
|
-
cmid: resource.cmid,
|
|
294
|
-
name: resource.name,
|
|
295
|
-
success,
|
|
296
|
-
});
|
|
297
|
-
if (success) {
|
|
298
|
-
log.success(` ✓ Completed: ${resource.name}`);
|
|
299
|
-
}
|
|
300
|
-
else {
|
|
301
|
-
log.error(` ✗ Failed: ${resource.name}`);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
const completed = results.filter(r => r.success).length;
|
|
305
|
-
const failed = results.filter(r => !r.success).length;
|
|
306
|
-
log.info(`\n執行結果: ${completed} 成功, ${failed} 失敗`);
|
|
307
|
-
if (output !== "silent") {
|
|
308
|
-
(0, index_js_1.formatAndOutput)(results, output, log);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
catch (e) {
|
|
312
|
-
apiContext.log.error(`Error: ${e instanceof Error ? e.message : String(e)}`);
|
|
313
|
-
process.exitCode = 1;
|
|
314
|
-
}
|
|
315
|
-
});
|
|
316
|
-
materialsCmd
|
|
317
|
-
.command("complete-all")
|
|
318
|
-
.description("Mark all incomplete resources (non-video) as complete across all in-progress courses")
|
|
319
|
-
.option("--dry-run", "Show what would be marked complete without doing it")
|
|
320
|
-
.option("--level <type>", "Course level: in_progress (default) | all", "in_progress")
|
|
321
|
-
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
322
|
-
.action(async (options, command) => {
|
|
323
|
-
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
324
|
-
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
325
|
-
if (!apiContext) {
|
|
326
|
-
process.exitCode = 1;
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
try {
|
|
330
|
-
const { log, session } = apiContext;
|
|
331
|
-
// Get user ID
|
|
332
|
-
const siteInfo = await (0, moodle_js_1.getSiteInfoApi)(session);
|
|
333
|
-
// Get all courses
|
|
334
|
-
const classification = options.level === "all" ? undefined : "inprogress";
|
|
335
|
-
const courses = await (0, moodle_js_1.getEnrolledCoursesApi)(session, { classification });
|
|
336
|
-
log.info(`Scanning ${courses.length} courses for incomplete resources...`);
|
|
337
|
-
const allResults = [];
|
|
338
|
-
let totalIncomplete = 0;
|
|
339
|
-
for (const course of courses) {
|
|
340
|
-
try {
|
|
341
|
-
// Get completion status for all activities in the course
|
|
342
|
-
const completionData = await (0, moodle_js_1.moodleApiCall)(session, "core_completion_get_activities_completion_status", { courseid: course.id, userid: siteInfo.userid });
|
|
343
|
-
if (!completionData?.statuses)
|
|
344
|
-
continue;
|
|
345
|
-
// Filter for resources (non-video) that have completion enabled but are not complete
|
|
346
|
-
const incompleteResources = completionData.statuses.filter((status) => {
|
|
347
|
-
if (status.modname === "supervideo")
|
|
348
|
-
return false;
|
|
349
|
-
if (!status.hascompletion)
|
|
350
|
-
return false;
|
|
351
|
-
if (status.isoverallcomplete)
|
|
352
|
-
return false;
|
|
353
|
-
return true;
|
|
354
|
-
});
|
|
355
|
-
if (incompleteResources.length > 0) {
|
|
356
|
-
log.info(`\n${course.fullname}: ${incompleteResources.length} incomplete resources`);
|
|
357
|
-
totalIncomplete += incompleteResources.length;
|
|
358
|
-
if (options.dryRun) {
|
|
359
|
-
for (const resource of incompleteResources) {
|
|
360
|
-
log.info(` - ${resource.name} (cmid: ${resource.cmid})`);
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
else {
|
|
364
|
-
for (const resource of incompleteResources) {
|
|
365
|
-
const success = await (0, moodle_js_1.updateActivityCompletionStatusManually)(session, resource.cmid, true);
|
|
366
|
-
allResults.push({
|
|
367
|
-
courseId: course.id,
|
|
368
|
-
courseName: course.fullname,
|
|
369
|
-
cmid: resource.cmid,
|
|
370
|
-
name: resource.name,
|
|
371
|
-
success,
|
|
372
|
-
});
|
|
373
|
-
if (success) {
|
|
374
|
-
log.success(` ✓ Completed: ${resource.name}`);
|
|
375
|
-
}
|
|
376
|
-
else {
|
|
377
|
-
log.error(` ✗ Failed: ${resource.name}`);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
catch (e) {
|
|
384
|
-
log.warn(`Failed to process course ${course.fullname}: ${e}`);
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
if (totalIncomplete === 0) {
|
|
388
|
-
log.info("\nAll resources are already complete (or no resources with completion tracking).");
|
|
389
|
-
return;
|
|
390
|
-
}
|
|
391
|
-
if (options.dryRun) {
|
|
392
|
-
log.info(`\n[Dry run] Found ${totalIncomplete} incomplete resources across ${courses.length} courses.`);
|
|
393
|
-
log.info("Run without --dry-run to mark them as complete.");
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
const completed = allResults.filter(r => r.success).length;
|
|
397
|
-
const failed = allResults.filter(r => !r.success).length;
|
|
398
|
-
log.info(`\n執行結果: ${completed} 成功, ${failed} 失敗`);
|
|
399
|
-
if (output !== "silent") {
|
|
400
|
-
(0, index_js_1.formatAndOutput)(allResults, output, log);
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
catch (e) {
|
|
404
|
-
apiContext.log.error(`Error: ${e instanceof Error ? e.message : String(e)}`);
|
|
405
|
-
process.exitCode = 1;
|
|
406
|
-
}
|
|
407
|
-
});
|
|
408
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"quizzes.d.ts","sourceRoot":"","sources":["../../../src/src/commands/quizzes.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsEpC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA2N7D"}
|