@learnpack/learnpack 5.0.30 → 5.0.32
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 +12 -12
- package/lib/commands/audit.js +15 -15
- package/lib/commands/init.js +39 -15
- package/lib/commands/translate.js +2 -10
- package/lib/managers/server/routes.js +38 -0
- package/lib/managers/telemetry.d.ts +2 -0
- package/lib/managers/telemetry.js +7 -0
- package/lib/utils/creatorUtilities.d.ts +2 -0
- package/lib/utils/creatorUtilities.js +48 -7
- package/lib/utils/rigoActions.d.ts +1 -0
- package/lib/utils/rigoActions.js +13 -2
- package/oclif.manifest.json +1 -1
- package/package.json +2 -1
- package/src/commands/audit.ts +449 -449
- package/src/commands/breakToken.ts +36 -36
- package/src/commands/init.ts +48 -17
- package/src/commands/publish.ts +312 -312
- package/src/commands/start.ts +361 -361
- package/src/commands/translate.ts +1 -20
- package/src/managers/config/index.ts +1 -0
- package/src/managers/server/routes.ts +61 -1
- package/src/managers/socket.ts +274 -274
- package/src/managers/telemetry.ts +10 -0
- package/src/models/action.ts +12 -12
- package/src/utils/console.ts +24 -24
- package/src/utils/creatorUtilities.ts +66 -6
- package/src/utils/rigoActions.ts +13 -1
package/README.md
CHANGED
@@ -21,7 +21,7 @@ $ npm install -g @learnpack/learnpack
|
|
21
21
|
$ learnpack COMMAND
|
22
22
|
running command...
|
23
23
|
$ learnpack (-v|--version|version)
|
24
|
-
@learnpack/learnpack/5.0.
|
24
|
+
@learnpack/learnpack/5.0.32 win32-x64 node-v22.14.0
|
25
25
|
$ learnpack --help [COMMAND]
|
26
26
|
USAGE
|
27
27
|
$ learnpack COMMAND
|
@@ -76,7 +76,7 @@ DESCRIPTION
|
|
76
76
|
12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)
|
77
77
|
```
|
78
78
|
|
79
|
-
_See code: [src\commands\audit.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
79
|
+
_See code: [src\commands\audit.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.32/src\commands\audit.ts)_
|
80
80
|
|
81
81
|
## `learnpack breakToken`
|
82
82
|
|
@@ -91,7 +91,7 @@ OPTIONS
|
|
91
91
|
-y, --yes Skip all prompts and initialize an empty project
|
92
92
|
```
|
93
93
|
|
94
|
-
_See code: [src\commands\breakToken.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
94
|
+
_See code: [src\commands\breakToken.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.32/src\commands\breakToken.ts)_
|
95
95
|
|
96
96
|
## `learnpack clean`
|
97
97
|
|
@@ -106,7 +106,7 @@ DESCRIPTION
|
|
106
106
|
Extra documentation goes here
|
107
107
|
```
|
108
108
|
|
109
|
-
_See code: [src\commands\clean.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
109
|
+
_See code: [src\commands\clean.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.32/src\commands\clean.ts)_
|
110
110
|
|
111
111
|
## `learnpack download [PACKAGE]`
|
112
112
|
|
@@ -124,7 +124,7 @@ DESCRIPTION
|
|
124
124
|
Extra documentation goes here
|
125
125
|
```
|
126
126
|
|
127
|
-
_See code: [src\commands\download.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
127
|
+
_See code: [src\commands\download.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.32/src\commands\download.ts)_
|
128
128
|
|
129
129
|
## `learnpack help [COMMAND]`
|
130
130
|
|
@@ -156,7 +156,7 @@ OPTIONS
|
|
156
156
|
-y, --yes Skip all prompts and initialize an empty project
|
157
157
|
```
|
158
158
|
|
159
|
-
_See code: [src\commands\init.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
159
|
+
_See code: [src\commands\init.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.32/src\commands\init.ts)_
|
160
160
|
|
161
161
|
## `learnpack login [PACKAGE]`
|
162
162
|
|
@@ -174,7 +174,7 @@ DESCRIPTION
|
|
174
174
|
Extra documentation goes here
|
175
175
|
```
|
176
176
|
|
177
|
-
_See code: [src\commands\login.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
177
|
+
_See code: [src\commands\login.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.32/src\commands\login.ts)_
|
178
178
|
|
179
179
|
## `learnpack logout [PACKAGE]`
|
180
180
|
|
@@ -192,7 +192,7 @@ DESCRIPTION
|
|
192
192
|
Extra documentation goes here
|
193
193
|
```
|
194
194
|
|
195
|
-
_See code: [src\commands\logout.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
195
|
+
_See code: [src\commands\logout.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.32/src\commands\logout.ts)_
|
196
196
|
|
197
197
|
## `learnpack plugins`
|
198
198
|
|
@@ -323,7 +323,7 @@ OPTIONS
|
|
323
323
|
-h, --help show CLI help
|
324
324
|
```
|
325
325
|
|
326
|
-
_See code: [src\commands\publish.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
326
|
+
_See code: [src\commands\publish.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.32/src\commands\publish.ts)_
|
327
327
|
|
328
328
|
## `learnpack start`
|
329
329
|
|
@@ -345,7 +345,7 @@ OPTIONS
|
|
345
345
|
-y, --yes Skip all prompts and initialize an empty project
|
346
346
|
```
|
347
347
|
|
348
|
-
_See code: [src\commands\start.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
348
|
+
_See code: [src\commands\start.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.32/src\commands\start.ts)_
|
349
349
|
|
350
350
|
## `learnpack test [EXERCISESLUG]`
|
351
351
|
|
@@ -362,7 +362,7 @@ OPTIONS
|
|
362
362
|
-y, --yes Skip all prompts and initialize an empty project
|
363
363
|
```
|
364
364
|
|
365
|
-
_See code: [src\commands\test.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
365
|
+
_See code: [src\commands\test.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.32/src\commands\test.ts)_
|
366
366
|
|
367
367
|
## `learnpack translate`
|
368
368
|
|
@@ -376,7 +376,7 @@ OPTIONS
|
|
376
376
|
-y, --yes Skip all prompts and initialize an empty project
|
377
377
|
```
|
378
378
|
|
379
|
-
_See code: [src\commands\translate.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
379
|
+
_See code: [src\commands\translate.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.32/src\commands\translate.ts)_
|
380
380
|
<!-- commandsstop -->
|
381
381
|
|
382
382
|
> > > > > > > 0cb3e56d84c197f9d008836bb573eade212b7e57
|
package/lib/commands/audit.js
CHANGED
@@ -329,21 +329,21 @@ class AuditCommand extends SessionCommand_1.default {
|
|
329
329
|
}
|
330
330
|
}
|
331
331
|
}
|
332
|
-
AuditCommand.description = `learnpack audit is the command in charge of creating an auditory of the repository
|
333
|
-
...
|
334
|
-
learnpack audit checks for the following information in a repository:
|
335
|
-
1. The configuration object has slug, repository and description. (Error)
|
336
|
-
2. The command learnpack clean has been run. (Error)
|
337
|
-
3. If a markdown or test file doesn't have any content. (Error)
|
338
|
-
4. The links are accessing to valid servers. (Error)
|
339
|
-
5. The relative images are working (If they have the shortest path to the image or if the images exists in the assets). (Error)
|
340
|
-
6. The external images are working (If they are pointing to a valid server). (Error)
|
341
|
-
7. The exercises directory names are valid. (Error)
|
342
|
-
8. If an exercise doesn't have a README file. (Error)
|
343
|
-
9. The exercises array (Of the config file) has content. (Error)
|
344
|
-
10. The exercses have the same translations. (Warning)
|
345
|
-
11. The .gitignore file exists. (Warning)
|
346
|
-
12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)
|
332
|
+
AuditCommand.description = `learnpack audit is the command in charge of creating an auditory of the repository
|
333
|
+
...
|
334
|
+
learnpack audit checks for the following information in a repository:
|
335
|
+
1. The configuration object has slug, repository and description. (Error)
|
336
|
+
2. The command learnpack clean has been run. (Error)
|
337
|
+
3. If a markdown or test file doesn't have any content. (Error)
|
338
|
+
4. The links are accessing to valid servers. (Error)
|
339
|
+
5. The relative images are working (If they have the shortest path to the image or if the images exists in the assets). (Error)
|
340
|
+
6. The external images are working (If they are pointing to a valid server). (Error)
|
341
|
+
7. The exercises directory names are valid. (Error)
|
342
|
+
8. If an exercise doesn't have a README file. (Error)
|
343
|
+
9. The exercises array (Of the config file) has content. (Error)
|
344
|
+
10. The exercses have the same translations. (Warning)
|
345
|
+
11. The .gitignore file exists. (Warning)
|
346
|
+
12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)
|
347
347
|
`;
|
348
348
|
AuditCommand.flags = {
|
349
349
|
// name: flags.string({char: 'n', description: 'name to print'}),
|
package/lib/commands/init.js
CHANGED
@@ -20,6 +20,16 @@ const durationByKind = {
|
|
20
20
|
quiz: 2,
|
21
21
|
read: 1,
|
22
22
|
};
|
23
|
+
const whichTargetAudience = async () => {
|
24
|
+
const res = await prompts([
|
25
|
+
{
|
26
|
+
type: "text",
|
27
|
+
name: "targetAudience",
|
28
|
+
message: "What is the target audience for this tutorial?",
|
29
|
+
},
|
30
|
+
]);
|
31
|
+
return res.targetAudience;
|
32
|
+
};
|
23
33
|
const initializeInteractiveCreation = async (rigoToken, courseInfo) => {
|
24
34
|
let prevInteractions = "";
|
25
35
|
let isReady = false;
|
@@ -105,9 +115,14 @@ const appendContentIndex = async () => {
|
|
105
115
|
const handleAILogic = async (tutorialDir, packageInfo) => {
|
106
116
|
fs.removeSync(path.join(tutorialDir, "exercises", "01-hello-world"));
|
107
117
|
let sessionPayload = await session_1.default.getPayload();
|
108
|
-
|
109
|
-
|
110
|
-
|
118
|
+
const sessionExists = sessionPayload && sessionPayload.rigobot;
|
119
|
+
const isValidToken = sessionExists && sessionPayload.rigobot.key ?
|
120
|
+
await (0, rigoActions_1.isValidRigoToken)(sessionPayload.rigobot.key) :
|
121
|
+
false;
|
122
|
+
const isValidBreathecodeToken = sessionExists && sessionPayload.token ?
|
123
|
+
await api_1.default.validateToken(sessionPayload.token) :
|
124
|
+
false;
|
125
|
+
if (!sessionExists || !isValidBreathecodeToken || !isValidToken) {
|
111
126
|
console_1.default.info("Almost there! First you need to login with 4Geeks.com to use AI Generation tool for creators. You can create a new account here: https://4geeks.com/creators");
|
112
127
|
try {
|
113
128
|
sessionPayload = await session_1.default.login();
|
@@ -129,14 +144,20 @@ const handleAILogic = async (tutorialDir, packageInfo) => {
|
|
129
144
|
process.exit(1);
|
130
145
|
}
|
131
146
|
console_1.default.success("🎉 Let's begin this learning journey!");
|
147
|
+
const targetAudience = await whichTargetAudience();
|
132
148
|
const contentIndex = await appendContentIndex();
|
133
149
|
let packageContext = `
|
134
150
|
\n
|
135
|
-
Title: ${packageInfo.title.us}
|
136
|
-
Description: ${packageInfo.description.us}
|
151
|
+
Title: "${packageInfo.title.us}"
|
152
|
+
Description: "${packageInfo.description.us}"
|
153
|
+
Target Audience: "${targetAudience}"
|
137
154
|
|
138
155
|
${contentIndex ?
|
139
|
-
`Content Index submitted by the user, use this to guide your creation
|
156
|
+
`Content Index submitted by the user, use this to guide your creation. Keep in mind that your tutorial should contain these topics:
|
157
|
+
---
|
158
|
+
${contentIndex}
|
159
|
+
---
|
160
|
+
` :
|
140
161
|
""}
|
141
162
|
|
142
163
|
`;
|
@@ -146,9 +167,9 @@ const handleAILogic = async (tutorialDir, packageInfo) => {
|
|
146
167
|
packageInfo.duration = duration;
|
147
168
|
packageInfo.difficulty = difficulty;
|
148
169
|
packageContext = `
|
149
|
-
Title: ${title}
|
150
|
-
Description: ${description}
|
151
|
-
|
170
|
+
Title: "${title}"
|
171
|
+
Description: "${description}"
|
172
|
+
Target Audience: "${targetAudience}"
|
152
173
|
List of exercises: ${steps.join(", ")}
|
153
174
|
`;
|
154
175
|
const exercisesDir = path.join(tutorialDir, "exercises");
|
@@ -238,16 +259,19 @@ const getChoices = async (empty) => {
|
|
238
259
|
{
|
239
260
|
type: "select",
|
240
261
|
name: "grading",
|
241
|
-
message: "
|
262
|
+
message: "How are you going to grade students or yourself?",
|
242
263
|
choices: [
|
243
264
|
{
|
244
|
-
title: "
|
245
|
-
value:
|
265
|
+
title: "No grading: No feedback or testing whatsoever, similar to a an interactive book.",
|
266
|
+
value: null,
|
246
267
|
},
|
247
|
-
{ title: "Isolated: Small separated exercises", value: "isolated" },
|
248
268
|
{
|
249
|
-
title: "
|
250
|
-
value:
|
269
|
+
title: "Isolated: Each step is a new separate exercise",
|
270
|
+
value: "isolated",
|
271
|
+
},
|
272
|
+
{
|
273
|
+
title: "Step by step: Each step builds on top of each other like an incremental tutorial",
|
274
|
+
value: "incremental",
|
251
275
|
},
|
252
276
|
],
|
253
277
|
},
|
@@ -7,6 +7,7 @@ const prompts = require("prompts");
|
|
7
7
|
const rigoActions_1 = require("../utils/rigoActions");
|
8
8
|
const session_1 = require("../managers/session");
|
9
9
|
const console_1 = require("../utils/console");
|
10
|
+
const creatorUtilities_1 = require("../utils/creatorUtilities");
|
10
11
|
// This function list the names of the exercise directories inside the ./exercises folder, if the exercises folder doesn't exist, it will look for the ./.learn/exercises folder
|
11
12
|
const listExercises = async () => {
|
12
13
|
const exercisesDir = path.join(process.cwd(), "exercises");
|
@@ -21,18 +22,10 @@ const listExercises = async () => {
|
|
21
22
|
return fs.statSync(path.join(learnExercisesDir, file)).isDirectory();
|
22
23
|
});
|
23
24
|
};
|
24
|
-
const cleanReadme = (readme) => {
|
25
|
-
// Replace <text> and </text> with nothing
|
26
|
-
return readme.replace(/<text>/g, "").replace(/<\/text>/g, "");
|
27
|
-
};
|
28
25
|
const getReadmeForExercise = async (exercise) => {
|
29
26
|
const readmePath = path.join(process.cwd(), "exercises", exercise, "README.md");
|
30
27
|
return fs.readFileSync(readmePath, "utf8");
|
31
28
|
};
|
32
|
-
const saveTranslatedReadme = async (exercise, languageCode, readme) => {
|
33
|
-
const readmePath = path.join(process.cwd(), "exercises", exercise, `README.${languageCode}.md`);
|
34
|
-
fs.writeFileSync(readmePath, cleanReadme(readme));
|
35
|
-
};
|
36
29
|
class BuildCommand extends SessionCommand_1.default {
|
37
30
|
async init() {
|
38
31
|
const { flags } = this.parse(BuildCommand);
|
@@ -86,8 +79,7 @@ class BuildCommand extends SessionCommand_1.default {
|
|
86
79
|
text_to_translate: readme,
|
87
80
|
output_language: language,
|
88
81
|
});
|
89
|
-
|
90
|
-
await saveTranslatedReadme(exercise, response.parsed.output_language_code, response.parsed.translation);
|
82
|
+
await (0, creatorUtilities_1.saveTranslatedReadme)(exercise, response.parsed.output_language_code, response.parsed.translation);
|
91
83
|
console_1.default.success(`Translated ${exercise} to ${language} successfully`);
|
92
84
|
}));
|
93
85
|
}));
|
@@ -11,6 +11,9 @@ const fileQueue_1 = require("../../utils/fileQueue");
|
|
11
11
|
const exercise_1 = require("../config/exercise");
|
12
12
|
const session_1 = require("../../managers/session");
|
13
13
|
const telemetry_1 = require("../telemetry");
|
14
|
+
const creatorUtilities_1 = require("../../utils/creatorUtilities");
|
15
|
+
const rigoActions_1 = require("../../utils/rigoActions");
|
16
|
+
// import { eventManager } from "../../utils/osOperations"
|
14
17
|
const withHandler = (func) => (req, res) => {
|
15
18
|
try {
|
16
19
|
func(req, res);
|
@@ -275,6 +278,41 @@ async function default_1(app, configObject, configManager) {
|
|
275
278
|
res.status(500).json({ error: "Failed to delete exercise" });
|
276
279
|
}
|
277
280
|
}));
|
281
|
+
app.post("/actions/translate", jsonBodyParser, withHandler(async (req, res) => {
|
282
|
+
var _a;
|
283
|
+
const { exerciseSlugs, languages } = req.body;
|
284
|
+
const session = await session_1.default.getPayload();
|
285
|
+
const rigoToken = (_a = session === null || session === void 0 ? void 0 : session.rigobot) === null || _a === void 0 ? void 0 : _a.key;
|
286
|
+
if (!rigoToken) {
|
287
|
+
return res.status(400).json({ error: "RigoToken not found" });
|
288
|
+
}
|
289
|
+
const languagesToTranslate = languages.split(",");
|
290
|
+
try {
|
291
|
+
await Promise.all(exerciseSlugs.map(async (slug) => {
|
292
|
+
const exercise = configManager.getExercise(slug);
|
293
|
+
if (!exercise) {
|
294
|
+
throw new Error(`Exercise ${slug} not found`);
|
295
|
+
}
|
296
|
+
if (exercise.getReadme) {
|
297
|
+
const readme = exercise.getReadme(null);
|
298
|
+
await Promise.all(languagesToTranslate.map(async (language) => {
|
299
|
+
const response = await (0, rigoActions_1.translateExercise)(rigoToken, {
|
300
|
+
text_to_translate: readme.body,
|
301
|
+
output_language: language,
|
302
|
+
});
|
303
|
+
await (0, creatorUtilities_1.saveTranslatedReadme)(slug, response.parsed.output_language_code, response.parsed.translation);
|
304
|
+
console_1.default.success(`Translated ${slug} to ${language} successfully`);
|
305
|
+
}));
|
306
|
+
}
|
307
|
+
}));
|
308
|
+
configManager.buildIndex();
|
309
|
+
return res.status(200).json({ message: "Translated exercises" });
|
310
|
+
}
|
311
|
+
catch (error) {
|
312
|
+
console.log(error, "ERROR");
|
313
|
+
return res.status(400).json({ error: error.message });
|
314
|
+
}
|
315
|
+
}));
|
278
316
|
app.post("/exercise/:slug/create", jsonBodyParser, withHandler(async (req, res) => {
|
279
317
|
const { title, readme, language } = req.body;
|
280
318
|
const { slug } = req.params;
|
@@ -53,6 +53,7 @@ type TStudent = {
|
|
53
53
|
};
|
54
54
|
export interface ITelemetryJSONSchema {
|
55
55
|
telemetry_id?: string;
|
56
|
+
version: string;
|
56
57
|
user_id?: number | string;
|
57
58
|
slug: string;
|
58
59
|
agent?: string;
|
@@ -77,6 +78,7 @@ interface ITelemetryManager {
|
|
77
78
|
user: TUser;
|
78
79
|
urls: TTelemetryUrls;
|
79
80
|
started: boolean;
|
81
|
+
version: string;
|
80
82
|
salute: (message: string) => void;
|
81
83
|
start: (agent: string, steps: TStep[], path: string, tutorialSlug: string) => void;
|
82
84
|
prevStep?: number;
|
@@ -1,6 +1,7 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
3
|
const api_1 = require("../utils/api");
|
4
|
+
const packageInfo = require("../../package.json");
|
4
5
|
const console_1 = require("../utils/console");
|
5
6
|
const fs = require("fs");
|
6
7
|
function createUUID() {
|
@@ -13,6 +14,7 @@ function stringToBase64(input) {
|
|
13
14
|
const TelemetryManager = {
|
14
15
|
current: null,
|
15
16
|
urls: {},
|
17
|
+
version: packageInfo.version,
|
16
18
|
user: {
|
17
19
|
token: "",
|
18
20
|
id: "",
|
@@ -37,6 +39,8 @@ const TelemetryManager = {
|
|
37
39
|
this.current = {
|
38
40
|
telemetry_id: createUUID(),
|
39
41
|
slug: tutorialSlug,
|
42
|
+
version: `CLI:${this.version}`,
|
43
|
+
user_id: 0,
|
40
44
|
agent,
|
41
45
|
tutorial_started_at: Date.now(),
|
42
46
|
last_interaction_at: Date.now(),
|
@@ -51,6 +55,9 @@ const TelemetryManager = {
|
|
51
55
|
if (this.current.user_id) {
|
52
56
|
this.user.id = this.current.user_id.toString();
|
53
57
|
}
|
58
|
+
if (!this.current.version) {
|
59
|
+
this.current.version = `CLI:${this.version}`;
|
60
|
+
}
|
54
61
|
this.save();
|
55
62
|
this.started = true;
|
56
63
|
console_1.default.debug("Telemetry started successfully!");
|
@@ -46,4 +46,6 @@ export declare const makePackageInfo: (choices: any) => {
|
|
46
46
|
export declare function estimateDuration(listOfSteps: string[]): number;
|
47
47
|
export declare function createFileOnDesktop(): Promise<void>;
|
48
48
|
export declare function getContentIndex(): string;
|
49
|
+
export declare function extractTextFromMarkdown(mdContent: string): string;
|
50
|
+
export declare const saveTranslatedReadme: (exercise: string, languageCode: string, readme: string) => Promise<void>;
|
49
51
|
export {};
|
@@ -1,13 +1,13 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
exports.makePackageInfo = exports.getExInfo = exports.estimateReadingTime = void 0;
|
3
|
+
exports.saveTranslatedReadme = exports.makePackageInfo = exports.getExInfo = exports.estimateReadingTime = void 0;
|
4
4
|
exports.checkReadingTime = checkReadingTime;
|
5
5
|
exports.extractImagesFromMarkdown = extractImagesFromMarkdown;
|
6
6
|
exports.getFilenameFromUrl = getFilenameFromUrl;
|
7
7
|
exports.estimateDuration = estimateDuration;
|
8
8
|
exports.createFileOnDesktop = createFileOnDesktop;
|
9
9
|
exports.getContentIndex = getContentIndex;
|
10
|
-
|
10
|
+
exports.extractTextFromMarkdown = extractTextFromMarkdown;
|
11
11
|
const frontMatter = require("front-matter");
|
12
12
|
const path = require("path");
|
13
13
|
const fs = require("fs");
|
@@ -41,6 +41,7 @@ exports.estimateReadingTime = estimateReadingTime;
|
|
41
41
|
function checkReadingTime(markdown, wordsPerMinute = 200, maxMinutes = 1) {
|
42
42
|
const parsed = frontMatter(markdown);
|
43
43
|
const readingTime = (0, exports.estimateReadingTime)(parsed.body, wordsPerMinute);
|
44
|
+
// const readingEase = estimateReadingEase(parsed.body)
|
44
45
|
let attributes = parsed.attributes ? parsed.attributes : {};
|
45
46
|
if (typeof parsed.attributes !== "object") {
|
46
47
|
attributes = {};
|
@@ -56,6 +57,7 @@ function checkReadingTime(markdown, wordsPerMinute = 200, maxMinutes = 1) {
|
|
56
57
|
exceedsThreshold: false,
|
57
58
|
minutes: 0,
|
58
59
|
body: "",
|
60
|
+
// readingEase: 0,
|
59
61
|
};
|
60
62
|
}
|
61
63
|
// Reconstruct the markdown with the front matter
|
@@ -65,6 +67,7 @@ function checkReadingTime(markdown, wordsPerMinute = 200, maxMinutes = 1) {
|
|
65
67
|
exceedsThreshold: readingTime.minutes > maxMinutes,
|
66
68
|
minutes: readingTime.minutes,
|
67
69
|
body: parsed.body,
|
70
|
+
// readingEase: 0,
|
68
71
|
};
|
69
72
|
}
|
70
73
|
const slugify = (text) => {
|
@@ -146,11 +149,13 @@ function estimateDuration(listOfSteps) {
|
|
146
149
|
}
|
147
150
|
const writeFilePromise = (0, util_1.promisify)(fs.writeFile);
|
148
151
|
const execPromise = (0, util_1.promisify)(child_process_1.exec);
|
149
|
-
const example_content = `
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
152
|
+
const example_content = `Write or paste your table of content below this line, each topic should be defined on a new line, here is an example:
|
153
|
+
|
154
|
+
Introduction to AI: Explain what is AI and its applications
|
155
|
+
Introduction to Machine Learning: Explain what is machine learning and its applications
|
156
|
+
What is an AI Model: Explain what is an AI model and its applications
|
157
|
+
How to use an AI Model: Different APIs, local models, etc.
|
158
|
+
How to build an AI Model: Fine-tuning, data collection, cleaning and more.
|
154
159
|
`;
|
155
160
|
async function createFileOnDesktop() {
|
156
161
|
try {
|
@@ -191,3 +196,39 @@ function getContentIndex() {
|
|
191
196
|
const content = fs.readFileSync(filePath, "utf8");
|
192
197
|
return content;
|
193
198
|
}
|
199
|
+
// export function fleschKincaidReadingEase(text: string): number {
|
200
|
+
// const sentences = text.split(/[.!?]/).filter((s) => s.trim().length > 0)
|
201
|
+
// const words = text.split(/\s+/).filter((w) => w.trim().length > 0)
|
202
|
+
// const totalSyllables = words.reduce((sum, word) => sum + syllable(word), 0)
|
203
|
+
// const ASL = words.length / sentences.length // Average Sentence Length
|
204
|
+
// const ASW = totalSyllables / words.length // Average Syllables per Word
|
205
|
+
// return Math.round(206.835 - 1.015 * ASL - 84.6 * ASW)
|
206
|
+
// }
|
207
|
+
function extractTextFromMarkdown(mdContent) {
|
208
|
+
let content = mdContent.replace(/!\[.*?]\(.*?\)/g, "");
|
209
|
+
content = content.replace(/\[.*?]\(.*?\)/g, "");
|
210
|
+
content = content.replace(/`.*?`/g, "");
|
211
|
+
content = content.replace(/```[\S\s]*?```/g, "");
|
212
|
+
return content.trim();
|
213
|
+
}
|
214
|
+
// export const estimateReadingEase = async (text: string): Promise<number> => {
|
215
|
+
// const cleanedText = extractTextFromMarkdown(text)
|
216
|
+
// // @ts-ignore
|
217
|
+
// const rs = (await import("text-readability")).default;
|
218
|
+
// // return fleschKincaidReadingEase(cleanedText)
|
219
|
+
// // const score = readability(cleanedText)
|
220
|
+
// // console.log(score)
|
221
|
+
// // return score.fleschKincaid ?? 0
|
222
|
+
// const score = rs.fleschReadingEase(cleanedText)
|
223
|
+
// console.log(score, "SCORE FLESCH READING EASE")
|
224
|
+
// return score
|
225
|
+
// }
|
226
|
+
const cleanReadme = (readme) => {
|
227
|
+
// Replace <text> and </text> with nothing
|
228
|
+
return readme.replace(/<text>/g, "").replace(/<\/text>/g, "");
|
229
|
+
};
|
230
|
+
const saveTranslatedReadme = async (exercise, languageCode, readme) => {
|
231
|
+
const readmePath = path.join(process.cwd(), "exercises", exercise, `README.${languageCode}.md`);
|
232
|
+
fs.writeFileSync(readmePath, cleanReadme(readme));
|
233
|
+
};
|
234
|
+
exports.saveTranslatedReadme = saveTranslatedReadme;
|
@@ -58,4 +58,5 @@ type TReduceReadmeInputs = {
|
|
58
58
|
expected_number_words: string;
|
59
59
|
};
|
60
60
|
export declare function reduceReadme(rigoToken: string, inputs: TReduceReadmeInputs): Promise<any>;
|
61
|
+
export declare const isValidRigoToken: (rigobotToken: string) => Promise<boolean>;
|
61
62
|
export {};
|
package/lib/utils/rigoActions.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
exports.readmeCreator = exports.createCodingReadme = exports.createCodeFile = exports.interactiveCreation = exports.generateCourseIntroduction = exports.translateExercise = exports.generateImage = exports.hasCreatorPermission = exports.createReadme = void 0;
|
3
|
+
exports.isValidRigoToken = exports.readmeCreator = exports.createCodingReadme = exports.createCodeFile = exports.interactiveCreation = exports.generateCourseIntroduction = exports.translateExercise = exports.generateImage = exports.hasCreatorPermission = exports.createReadme = void 0;
|
4
4
|
exports.downloadImage = downloadImage;
|
5
5
|
exports.createPreviewReadme = createPreviewReadme;
|
6
6
|
exports.reduceReadme = reduceReadme;
|
@@ -46,7 +46,7 @@ const hasCreatorPermission = async (token) => {
|
|
46
46
|
return true;
|
47
47
|
}
|
48
48
|
catch (error) {
|
49
|
-
console_1.default.
|
49
|
+
console_1.default.error(error);
|
50
50
|
return false;
|
51
51
|
}
|
52
52
|
};
|
@@ -191,3 +191,14 @@ async function reduceReadme(rigoToken, inputs) {
|
|
191
191
|
return null;
|
192
192
|
}
|
193
193
|
}
|
194
|
+
const isValidRigoToken = async (rigobotToken) => {
|
195
|
+
const rigoUrl = `${RIGOBOT_HOST}/v1/auth/token/${rigobotToken}`;
|
196
|
+
const rigoResp = await fetch(rigoUrl);
|
197
|
+
if (!rigoResp.ok) {
|
198
|
+
console_1.default.debug("Invalid Rigobot token", rigoResp.status);
|
199
|
+
console_1.default.debug(rigoResp.statusText);
|
200
|
+
return false;
|
201
|
+
}
|
202
|
+
return true;
|
203
|
+
};
|
204
|
+
exports.isValidRigoToken = isValidRigoToken;
|
package/oclif.manifest.json
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":"5.0.
|
1
|
+
{"version":"5.0.32","commands":{"audit":{"id":"audit","description":"learnpack audit is the command in charge of creating an auditory of the repository\n...\nlearnpack audit checks for the following information in a repository:\n 1. The configuration object has slug, repository and description. (Error)\n 2. The command learnpack clean has been run. (Error)\n 3. If a markdown or test file doesn't have any content. (Error)\n 4. The links are accessing to valid servers. (Error)\n 5. The relative images are working (If they have the shortest path to the image or if the images exists in the assets). (Error)\n 6. The external images are working (If they are pointing to a valid server). (Error)\n 7. The exercises directory names are valid. (Error)\n 8. If an exercise doesn't have a README file. (Error)\n 9. The exercises array (Of the config file) has content. (Error)\n 10. The exercses have the same translations. (Warning)\n 11. The .gitignore file exists. (Warning)\n 12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[]},"breakToken":{"id":"breakToken","description":"Break the token","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"grading":{"name":"grading","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"clean":{"id":"clean","description":"Clean the configuration object\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[]},"download":{"id":"download","description":"Describe the command here\n...\nExtra documentation goes here\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"init":{"id":"init","description":"Create a new learning package: Book, Tutorial or Exercise","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"grading":{"name":"grading","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"login":{"id":"login","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"logout":{"id":"logout","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"publish":{"id":"publish","description":"Builds the project by copying necessary files and directories into a zip file","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"start":{"id":"start","description":"Runs a small server with all the exercise instructions","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"port":{"name":"port","type":"option","char":"p","description":"server port"},"host":{"name":"host","type":"option","char":"h","description":"server host"},"disableGrading":{"name":"disableGrading","type":"boolean","char":"D","description":"disble grading functionality","allowNo":false},"watch":{"name":"watch","type":"boolean","char":"w","description":"Watch for file changes","allowNo":false},"editor":{"name":"editor","type":"option","char":"e","description":"[preview, extension]","options":["extension","preview"]},"version":{"name":"version","type":"option","char":"v","description":"E.g: 1.0.1"},"grading":{"name":"grading","type":"option","char":"g","description":"[isolated, incremental]","options":["isolated","incremental"]},"debug":{"name":"debug","type":"boolean","char":"d","description":"debugger mode for more verbage","allowNo":false}},"args":[]},"test":{"id":"test","description":"Test exercises","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false}},"args":[{"name":"exerciseSlug","description":"The name of the exercise to test","required":false,"hidden":false}]},"translate":{"id":"translate","description":"List all the lessons, the user is able of select many of them to translate to the given languages","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false}},"args":[]}}}
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@learnpack/learnpack",
|
3
3
|
"description": "Seamlessly build, sell and/or take interactive & auto-graded tutorials, start learning now or build a new tutorial to your audience.",
|
4
|
-
"version": "5.0.
|
4
|
+
"version": "5.0.32",
|
5
5
|
"author": "Alejandro Sanchez @alesanchezr",
|
6
6
|
"contributors": [
|
7
7
|
{
|
@@ -50,6 +50,7 @@
|
|
50
50
|
"shelljs": "^0.8.4",
|
51
51
|
"socket.io": "^4.4.1",
|
52
52
|
"targz": "^1.0.1",
|
53
|
+
"text-readability": "^1.1.0",
|
53
54
|
"tslib": "^1",
|
54
55
|
"validator": "^13.1.1",
|
55
56
|
"xxhashjs": "^0.2.2"
|