@learnpack/learnpack 5.0.215 → 5.0.231
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 +44 -39
- package/lib/commands/publish.d.ts +1 -1
- package/lib/commands/publish.js +17 -9
- package/lib/commands/serve.js +94 -31
- package/lib/creatorDist/assets/{index-DxwqeFD3.js → index-BjBYI-9r.js} +4401 -4319
- package/lib/creatorDist/assets/{index-Bnq3eZ3T.css → index-DmpsXknz.css} +6 -0
- package/lib/creatorDist/index.html +2 -2
- package/lib/utils/api.d.ts +7 -0
- package/lib/utils/api.js +54 -1
- package/oclif.manifest.json +1 -1
- package/package.json +3 -3
- package/src/commands/publish.ts +20 -8
- package/src/commands/serve.ts +109 -30
- package/src/creator/src/App.tsx +51 -9
- package/src/creator/src/components/FileUploader.tsx +35 -3
- package/src/creator/src/components/syllabus/SyllabusEditor.tsx +35 -20
- package/src/creator/src/locales/en.json +8 -3
- package/src/creator/src/locales/es.json +7 -2
- package/src/creator/src/utils/constants.ts +2 -1
- package/src/creator/src/utils/lib.ts +14 -1
- package/src/creator/src/utils/store.ts +10 -0
- package/src/creatorDist/assets/{index-DxwqeFD3.js → index-BjBYI-9r.js} +4401 -4319
- package/src/creatorDist/assets/{index-Bnq3eZ3T.css → index-DmpsXknz.css} +6 -0
- package/src/creatorDist/index.html +2 -2
- package/src/ui/_app/app.css +1 -1
- package/src/ui/_app/app.js +373 -373
- package/src/ui/app.tar.gz +0 -0
- package/src/utils/api.ts +80 -10
@@ -518,6 +518,9 @@
|
|
518
518
|
.mb-6 {
|
519
519
|
margin-bottom: calc(var(--spacing) * 6);
|
520
520
|
}
|
521
|
+
.ml-2 {
|
522
|
+
margin-left: calc(var(--spacing) * 2);
|
523
|
+
}
|
521
524
|
.ml-6 {
|
522
525
|
margin-left: calc(var(--spacing) * 6);
|
523
526
|
}
|
@@ -1022,6 +1025,9 @@
|
|
1022
1025
|
.whitespace-pre-line {
|
1023
1026
|
white-space: pre-line;
|
1024
1027
|
}
|
1028
|
+
.text-black {
|
1029
|
+
color: var(--color-black);
|
1030
|
+
}
|
1025
1031
|
.text-blue-400 {
|
1026
1032
|
color: var(--color-blue-400);
|
1027
1033
|
}
|
@@ -10,8 +10,8 @@
|
|
10
10
|
/>
|
11
11
|
|
12
12
|
<title>Learnpack Creator: Craft tutorials in seconds!</title>
|
13
|
-
<script type="module" crossorigin src="/creator/assets/index-
|
14
|
-
<link rel="stylesheet" crossorigin href="/creator/assets/index-
|
13
|
+
<script type="module" crossorigin src="/creator/assets/index-BjBYI-9r.js"></script>
|
14
|
+
<link rel="stylesheet" crossorigin href="/creator/assets/index-DmpsXknz.css">
|
15
15
|
</head>
|
16
16
|
<body>
|
17
17
|
<div id="root"></div>
|
package/lib/utils/api.d.ts
CHANGED
@@ -28,6 +28,12 @@ export declare const doesAssetExists: (token: string, assetSlug: string) => Prom
|
|
28
28
|
exists: boolean;
|
29
29
|
academyId?: number;
|
30
30
|
}>;
|
31
|
+
type TTechnology = {
|
32
|
+
slug: string;
|
33
|
+
lang: string;
|
34
|
+
};
|
35
|
+
export declare const fetchTechnologies: () => Promise<any[]>;
|
36
|
+
export declare const getCurrentTechnologies: () => TTechnology[];
|
31
37
|
declare const _default: {
|
32
38
|
login: (identification: string, password: string) => Promise<any>;
|
33
39
|
publish: (config: any) => Promise<any>;
|
@@ -51,5 +57,6 @@ declare const _default: {
|
|
51
57
|
getCategories: (token: string) => Promise<any>;
|
52
58
|
updateRigoAssetID: (token: string, slug: string, asset_id: number) => Promise<any>;
|
53
59
|
createRigoPackage: (token: string, slug: string, config: any) => Promise<any>;
|
60
|
+
getCurrentTechnologies: () => TTechnology[];
|
54
61
|
};
|
55
62
|
export default _default;
|
package/lib/utils/api.js
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
exports.doesAssetExists = exports.createAsset = exports.validateToken = exports.listUserAcademies = exports.getConsumable = exports.countConsumables = exports.RIGOBOT_HOST = void 0;
|
3
|
+
exports.getCurrentTechnologies = exports.fetchTechnologies = exports.doesAssetExists = exports.createAsset = exports.validateToken = exports.listUserAcademies = exports.getConsumable = exports.countConsumables = exports.RIGOBOT_HOST = void 0;
|
4
4
|
const console_1 = require("../utils/console");
|
5
5
|
const storage = require("node-persist");
|
6
6
|
const cli_ux_1 = require("cli-ux");
|
7
7
|
const axios_1 = require("axios");
|
8
|
+
const dotenv = require("dotenv");
|
9
|
+
dotenv.config();
|
8
10
|
const HOST = "https://breathecode.herokuapp.com";
|
9
11
|
exports.RIGOBOT_HOST = "https://rigobot.herokuapp.com";
|
10
12
|
// export const RIGOBOT_HOST = "https://rigobot-test-cca7d841c9d8.herokuapp.com"
|
@@ -442,6 +444,56 @@ const createRigoPackage = async (token, slug, config) => {
|
|
442
444
|
throw error;
|
443
445
|
}
|
444
446
|
};
|
447
|
+
let technologiesCache = [];
|
448
|
+
const fetchTechnologies = async () => {
|
449
|
+
const BREATHECODE_PERMANENT_TOKEN = process.env.BREATHECODE_PERMANENT_TOKEN;
|
450
|
+
const LANGS = ["en", "es", "us"];
|
451
|
+
if (!BREATHECODE_PERMANENT_TOKEN) {
|
452
|
+
throw new Error("BREATHECODE_PERMANENT_TOKEN is not defined in environment variables");
|
453
|
+
}
|
454
|
+
const headers = {
|
455
|
+
Authorization: `Token ${BREATHECODE_PERMANENT_TOKEN}`,
|
456
|
+
};
|
457
|
+
const results = await Promise.all(LANGS.map(lang => axios_1.default
|
458
|
+
.get(`${HOST}/v1/registry/technology?lang=${lang}`, { headers })
|
459
|
+
.then(res => {
|
460
|
+
return res.data;
|
461
|
+
})
|
462
|
+
.then(data => data.map((item) => ({
|
463
|
+
slug: item.slug,
|
464
|
+
lang: lang,
|
465
|
+
})))));
|
466
|
+
const allItems = results.flat();
|
467
|
+
// Remove duplicates by slug+lang combination
|
468
|
+
const unique = [];
|
469
|
+
const seen = new Set();
|
470
|
+
for (const item of allItems) {
|
471
|
+
const key = `${item.slug}:${item.lang}`;
|
472
|
+
if (!seen.has(key)) {
|
473
|
+
seen.add(key);
|
474
|
+
unique.push(item);
|
475
|
+
}
|
476
|
+
}
|
477
|
+
return unique;
|
478
|
+
};
|
479
|
+
exports.fetchTechnologies = fetchTechnologies;
|
480
|
+
// Function to update the cache and schedule the next update
|
481
|
+
async function updateTechnologiesPeriodically() {
|
482
|
+
try {
|
483
|
+
technologiesCache = await (0, exports.fetchTechnologies)();
|
484
|
+
// Uncomment for debugging:
|
485
|
+
// console.log('Technologies list updated:', technologiesCache);
|
486
|
+
}
|
487
|
+
catch (error) {
|
488
|
+
console.error("Error updating technologies list:", error);
|
489
|
+
}
|
490
|
+
finally {
|
491
|
+
setTimeout(updateTechnologiesPeriodically, 24 * 60 * 60 * 1000);
|
492
|
+
}
|
493
|
+
}
|
494
|
+
updateTechnologiesPeriodically();
|
495
|
+
const getCurrentTechnologies = () => technologiesCache;
|
496
|
+
exports.getCurrentTechnologies = getCurrentTechnologies;
|
445
497
|
exports.default = {
|
446
498
|
login,
|
447
499
|
publish,
|
@@ -459,4 +511,5 @@ exports.default = {
|
|
459
511
|
getCategories,
|
460
512
|
updateRigoAssetID,
|
461
513
|
createRigoPackage,
|
514
|
+
getCurrentTechnologies: exports.getCurrentTechnologies,
|
462
515
|
};
|
package/oclif.manifest.json
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":"5.0.
|
1
|
+
{"version":"5.0.231","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":{"strict":{"name":"strict","type":"boolean","char":"s","description":"strict mode","allowNo":false}},"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":{"strict":{"name":"strict","type":"boolean","char":"s","description":"strict mode","allowNo":false},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"serve":{"id":"serve","description":"Runs a small server to build tutorials","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"},"debug":{"name":"debug","type":"boolean","char":"d","description":"debugger mode for more verbage","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.231",
|
5
5
|
"author": "Alejandro Sanchez @alesanchezr",
|
6
6
|
"contributors": [
|
7
7
|
{
|
@@ -154,10 +154,10 @@
|
|
154
154
|
]
|
155
155
|
},
|
156
156
|
"scripts": {
|
157
|
-
"copy-assets": "npx cpy src/creatorDist/**/* lib/creatorDist --parents",
|
157
|
+
"copy-assets": "npx cpy src/creatorDist/**/* lib/creatorDist --parents --verbose",
|
158
158
|
"tsc": "tsc -b",
|
159
159
|
"postpack": "rm -f oclif.manifest.json && eslint . --ext .ts --config .eslintrc",
|
160
|
-
"prepack": "rm -rf lib && tsc -b && npm run copy-assets
|
160
|
+
"prepack": "rm -rf lib && tsc -b && npm run copy-assets",
|
161
161
|
"pre": "node ./test/precommit/index.ts",
|
162
162
|
"test": "NODE_ENV=test nyc --extension .ts mocha --forbid-only \"test/**/*.test.ts\"",
|
163
163
|
"version": "oclif-dev readme && git add README.md",
|
package/src/commands/publish.ts
CHANGED
@@ -25,8 +25,20 @@ const uploadZipEndpont = RIGOBOT_HOST + "/v1/learnpack/upload"
|
|
25
25
|
export const handleAssetCreation = async (
|
26
26
|
sessionPayload: { token: string; rigobotToken: string },
|
27
27
|
learnJson: any,
|
28
|
+
selectedLang: string,
|
28
29
|
learnpackDeployUrl: string
|
29
30
|
) => {
|
31
|
+
const categories: Record<string, number> = {
|
32
|
+
us: 91,
|
33
|
+
es: 92,
|
34
|
+
}
|
35
|
+
|
36
|
+
let category = categories[selectedLang]
|
37
|
+
|
38
|
+
if (!category) {
|
39
|
+
category = 91
|
40
|
+
}
|
41
|
+
|
30
42
|
try {
|
31
43
|
const user = await api.validateToken(sessionPayload.token)
|
32
44
|
|
@@ -39,13 +51,13 @@ export const handleAssetCreation = async (
|
|
39
51
|
Console.info("Asset does not exist in this academy, creating it")
|
40
52
|
const asset = await api.createAsset(sessionPayload.token, {
|
41
53
|
slug: learnJson.slug,
|
42
|
-
title: learnJson.title
|
43
|
-
lang:
|
44
|
-
description: learnJson.description
|
54
|
+
title: learnJson.title[selectedLang],
|
55
|
+
lang: selectedLang,
|
56
|
+
description: learnJson.description[selectedLang],
|
45
57
|
learnpack_deploy_url: learnpackDeployUrl,
|
46
|
-
technologies:
|
58
|
+
technologies: learnJson.technologies,
|
47
59
|
url: learnpackDeployUrl,
|
48
|
-
category:
|
60
|
+
category: category,
|
49
61
|
owner: user.id,
|
50
62
|
author: user.id,
|
51
63
|
preview: learnJson.preview,
|
@@ -63,8 +75,8 @@ export const handleAssetCreation = async (
|
|
63
75
|
learnJson.slug,
|
64
76
|
{
|
65
77
|
learnpack_deploy_url: learnpackDeployUrl,
|
66
|
-
title: learnJson.title
|
67
|
-
description: learnJson.description
|
78
|
+
title: learnJson.title[selectedLang],
|
79
|
+
description: learnJson.description[selectedLang],
|
68
80
|
}
|
69
81
|
)
|
70
82
|
await api.updateRigoAssetID(
|
@@ -423,7 +435,7 @@ class BuildCommand extends SessionCommand {
|
|
423
435
|
fs.unlinkSync(zipFilePath)
|
424
436
|
this.removeDirectory(buildDir)
|
425
437
|
|
426
|
-
await handleAssetCreation(sessionPayload, learnJson, res.data.url)
|
438
|
+
await handleAssetCreation(sessionPayload, learnJson, "us", res.data.url)
|
427
439
|
} catch (error) {
|
428
440
|
if (axios.isAxiosError(error)) {
|
429
441
|
if (error.response && error.response.status === 403) {
|
package/src/commands/serve.ts
CHANGED
@@ -102,7 +102,7 @@ const uploadFileToBucket = async (
|
|
102
102
|
path: string
|
103
103
|
) => {
|
104
104
|
const fileRef = bucket.file(path)
|
105
|
-
await fileRef.save(file)
|
105
|
+
await fileRef.save(Buffer.from(file, "utf8"))
|
106
106
|
}
|
107
107
|
|
108
108
|
const uploadImageToBucket = async (
|
@@ -231,6 +231,8 @@ async function startExerciseGeneration(
|
|
231
231
|
|
232
232
|
const webhookUrl = `${process.env.HOST}/webhooks/${courseSlug}/exercise-processor/${exercise.id}/${rigoToken}`
|
233
233
|
|
234
|
+
console.log("WEBHOOK URL", webhookUrl)
|
235
|
+
|
234
236
|
await readmeCreator(
|
235
237
|
rigoToken,
|
236
238
|
{
|
@@ -453,7 +455,7 @@ export default class ServeCommand extends SessionCommand {
|
|
453
455
|
|
454
456
|
// Servir archivos estáticos
|
455
457
|
// app.use(express.static(distPath))
|
456
|
-
app.use(express.json())
|
458
|
+
app.use(express.json({ limit: "20mb" }))
|
457
459
|
app.use(cors())
|
458
460
|
|
459
461
|
const appPath = path.resolve(__dirname, "../ui/_app")
|
@@ -603,6 +605,41 @@ export default class ServeCommand extends SessionCommand {
|
|
603
605
|
res.json({ id, status: "SUCCESS" })
|
604
606
|
})
|
605
607
|
|
608
|
+
app.post("/webhooks/:courseSlug/images/:imageId", async (req, res) => {
|
609
|
+
const { courseSlug, imageId } = req.params
|
610
|
+
const body = req.body
|
611
|
+
console.log("RECEIVING IMAGE WEBHOOK", body)
|
612
|
+
|
613
|
+
const imageUrl = body.image_url
|
614
|
+
const imagePath = `courses/${courseSlug}/.learn/assets/${imageId}`
|
615
|
+
|
616
|
+
const imageFile = bucket.file(imagePath)
|
617
|
+
const [exists] = await imageFile.exists()
|
618
|
+
if (!exists) {
|
619
|
+
// Descargar la imagen
|
620
|
+
const response = await fetch(imageUrl)
|
621
|
+
if (!response.ok) {
|
622
|
+
return res.status(400).json({
|
623
|
+
status: "ERROR",
|
624
|
+
message: "No se pudo descargar la imagen",
|
625
|
+
})
|
626
|
+
}
|
627
|
+
|
628
|
+
const buffer = await response.arrayBuffer()
|
629
|
+
|
630
|
+
// Guardar la imagen en el bucket
|
631
|
+
await imageFile.save(new Uint8Array(buffer), {
|
632
|
+
contentType: "image/png",
|
633
|
+
})
|
634
|
+
}
|
635
|
+
|
636
|
+
emitToNotification(imageId, {
|
637
|
+
status: "SUCCESS",
|
638
|
+
message: "Image generated successfully",
|
639
|
+
})
|
640
|
+
res.json({ status: "SUCCESS" })
|
641
|
+
})
|
642
|
+
|
606
643
|
app.post(
|
607
644
|
"/webhooks/:courseSlug/exercise-processor/:lessonID/:rigoToken",
|
608
645
|
async (req, res) => {
|
@@ -1088,33 +1125,40 @@ export default class ServeCommand extends SessionCommand {
|
|
1088
1125
|
}
|
1089
1126
|
})
|
1090
1127
|
|
1091
|
-
app.get("/test/:slug", (req, res) => {
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1128
|
+
// app.get("/test/:slug", (req, res) => {
|
1129
|
+
// emitToCourse(req.params.slug, "course-creation", {
|
1130
|
+
// lesson: "000-welcome-to-bird-domestication",
|
1131
|
+
// status: "generating",
|
1132
|
+
// log: "Hello",
|
1133
|
+
// })
|
1134
|
+
// emitToCourse(req.params.slug, "course-creation", {
|
1135
|
+
// lesson: "000-welcome-to-bird-domestication",
|
1136
|
+
// status: "generating",
|
1137
|
+
// log: "Hello",
|
1138
|
+
// })
|
1139
|
+
// emitToCourse(req.params.slug, "course-creation", {
|
1140
|
+
// lesson: "000-welcome-to-bird-domestication",
|
1141
|
+
// status: "generating",
|
1142
|
+
// log: "Hello broder",
|
1143
|
+
// })
|
1144
|
+
// emitToCourse(req.params.slug, "course-creation", {
|
1145
|
+
// lesson: "000-welcome-to-bird-domestication",
|
1146
|
+
// status: "done",
|
1147
|
+
// log: "Hello broder",
|
1148
|
+
// })
|
1149
|
+
// emitToCourse(req.params.slug, "course-creation", {
|
1150
|
+
// lesson: "other",
|
1151
|
+
// status: "generating",
|
1152
|
+
// log: "Hello",
|
1153
|
+
// })
|
1154
|
+
// res.send({ message: "Course creation started" })
|
1155
|
+
// })
|
1156
|
+
|
1157
|
+
app.get("/technologies", async (req, res) => {
|
1158
|
+
console.log("GET /technologies")
|
1159
|
+
|
1160
|
+
const technologies = await api.getCurrentTechnologies()
|
1161
|
+
res.json(technologies)
|
1118
1162
|
})
|
1119
1163
|
|
1120
1164
|
app.get("/assets/:file", (req, res) => {
|
@@ -1245,6 +1289,41 @@ export default class ServeCommand extends SessionCommand {
|
|
1245
1289
|
})
|
1246
1290
|
})
|
1247
1291
|
|
1292
|
+
// app.post(
|
1293
|
+
// "/check-latex/:courseSlug/:exerciseSlug/:lang",
|
1294
|
+
// async (req, res) => {
|
1295
|
+
// const { courseSlug, exerciseSlug, lang } = req.params
|
1296
|
+
|
1297
|
+
// const rigoToken = req.header("x-rigo-token")
|
1298
|
+
|
1299
|
+
// if (!rigoToken) {
|
1300
|
+
// return res.status(400).json({ error: "Missing tokens" })
|
1301
|
+
// }
|
1302
|
+
|
1303
|
+
// const exercise = await bucket.file(
|
1304
|
+
// `courses/${courseSlug}/exercises/${exerciseSlug}/README.${lang}.md`
|
1305
|
+
// )
|
1306
|
+
// const [content] = await exercise.download()
|
1307
|
+
// const headers = {
|
1308
|
+
// Authorization: `Token ${rigoToken}`,
|
1309
|
+
// }
|
1310
|
+
// const response = await axios.get(
|
1311
|
+
// `${RIGOBOT_HOST}/v1/prompting/completion/60865/`,
|
1312
|
+
// {
|
1313
|
+
// headers,
|
1314
|
+
// }
|
1315
|
+
// )
|
1316
|
+
|
1317
|
+
// console.log(response.data.parsed.content, "RESPONSE from Rigobot")
|
1318
|
+
|
1319
|
+
// res.json({
|
1320
|
+
// message: "Exercise downloaded",
|
1321
|
+
// completion: response.data,
|
1322
|
+
// exercise: content.toString(),
|
1323
|
+
// })
|
1324
|
+
// }
|
1325
|
+
// )
|
1326
|
+
|
1248
1327
|
app.get("/courses/:courseSlug/syllabus", async (req, res) => {
|
1249
1328
|
try {
|
1250
1329
|
console.log("GET /courses/:courseSlug/syllabus")
|
@@ -1489,11 +1568,11 @@ export default class ServeCommand extends SessionCommand {
|
|
1489
1568
|
await handleAssetCreation(
|
1490
1569
|
{ token: bcToken, rigobotToken: rigoToken },
|
1491
1570
|
fullConfig.config,
|
1571
|
+
selectedLang,
|
1492
1572
|
rigoRes.data.url
|
1493
1573
|
)
|
1494
1574
|
|
1495
1575
|
rimraf.sync(tmpRoot)
|
1496
|
-
console.log("RIGO RES", rigoRes.data)
|
1497
1576
|
|
1498
1577
|
return res.json({ url: rigoRes.data.url })
|
1499
1578
|
})
|
package/src/creator/src/App.tsx
CHANGED
@@ -13,6 +13,7 @@ import {
|
|
13
13
|
loginWithToken,
|
14
14
|
parseLesson,
|
15
15
|
fixTitleLength,
|
16
|
+
getTechnologies,
|
16
17
|
} from "./utils/lib"
|
17
18
|
|
18
19
|
import { Uploader } from "./components/Uploader"
|
@@ -42,11 +43,15 @@ function App() {
|
|
42
43
|
resetFormState,
|
43
44
|
cleanAll,
|
44
45
|
setMessages,
|
46
|
+
technologies,
|
47
|
+
setTechnologies,
|
45
48
|
} = useStore(
|
46
49
|
useShallow((state) => ({
|
47
50
|
formState: state.formState,
|
48
51
|
setFormState: state.setFormState,
|
49
52
|
setAuth: state.setAuth,
|
53
|
+
technologies: state.technologies,
|
54
|
+
setTechnologies: state.setTechnologies,
|
50
55
|
push: state.push,
|
51
56
|
history: state.history,
|
52
57
|
cleanHistory: state.cleanHistory,
|
@@ -67,7 +72,8 @@ function App() {
|
|
67
72
|
|
68
73
|
useEffect(() => {
|
69
74
|
verifyToken()
|
70
|
-
|
75
|
+
checkQueryParams()
|
76
|
+
checkTechs()
|
71
77
|
}, [])
|
72
78
|
|
73
79
|
const verifyToken = async () => {
|
@@ -84,13 +90,21 @@ function App() {
|
|
84
90
|
}
|
85
91
|
}
|
86
92
|
|
87
|
-
const
|
88
|
-
const {
|
93
|
+
const checkQueryParams = () => {
|
94
|
+
const {
|
95
|
+
description,
|
96
|
+
duration,
|
97
|
+
plan,
|
98
|
+
purpose,
|
99
|
+
language,
|
100
|
+
new: newParam,
|
101
|
+
} = checkParams([
|
89
102
|
"description",
|
90
103
|
"duration",
|
91
104
|
"plan",
|
92
105
|
"purpose",
|
93
106
|
"language",
|
107
|
+
"new",
|
94
108
|
])
|
95
109
|
if (description) {
|
96
110
|
console.log("description", description)
|
@@ -130,12 +144,19 @@ function App() {
|
|
130
144
|
currentStep: "hasContentIndex",
|
131
145
|
})
|
132
146
|
}
|
147
|
+
|
148
|
+
if (newParam && newParam.toLowerCase().trim() === "true") {
|
149
|
+
cleanAll()
|
150
|
+
}
|
133
151
|
}
|
134
152
|
|
135
153
|
const handleCreateTutorial = async () => {
|
136
154
|
try {
|
137
|
-
|
138
|
-
if (
|
155
|
+
let isAuthenticated = false
|
156
|
+
if (auth.publicToken) {
|
157
|
+
isAuthenticated = await isValidRigoToken(auth.publicToken)
|
158
|
+
}
|
159
|
+
if (!isAuthenticated && auth.rigoToken) {
|
139
160
|
setAuth({
|
140
161
|
...auth,
|
141
162
|
rigoToken: "",
|
@@ -145,14 +166,24 @@ function App() {
|
|
145
166
|
})
|
146
167
|
}
|
147
168
|
|
169
|
+
let techs = technologies.filter((t) => t.lang === formState.language)
|
170
|
+
|
171
|
+
if (techs.length === 0) {
|
172
|
+
techs = technologies.filter((t) => t.lang === "us")
|
173
|
+
}
|
174
|
+
|
148
175
|
const res = await publicInteractiveCreation(
|
149
176
|
{
|
150
|
-
courseInfo: `${JSON.stringify(
|
177
|
+
courseInfo: `${JSON.stringify(
|
178
|
+
formState
|
179
|
+
)}. The following technologies are available, choose up to 3 from the following list: <techs>${techs
|
180
|
+
.map((t) => t.slug)
|
181
|
+
.join(", ")}</techs>`,
|
151
182
|
prevInteractions: "USER: " + formState.description,
|
152
183
|
},
|
153
|
-
auth.rigoToken &&
|
184
|
+
auth.rigoToken && isAuthenticated ? auth.rigoToken : auth.publicToken,
|
154
185
|
formState.purpose || "learnpack-lesson-writer",
|
155
|
-
auth.rigoToken &&
|
186
|
+
auth.rigoToken && isAuthenticated ? false : true
|
156
187
|
)
|
157
188
|
const lessons = res.parsed.listOfSteps.map((lesson: any) => {
|
158
189
|
return parseLesson(lesson, [])
|
@@ -165,7 +196,10 @@ function App() {
|
|
165
196
|
title: fixTitleLength(res.parsed.title),
|
166
197
|
description: res.parsed.description,
|
167
198
|
language: res.parsed.languageCode || formState.language || "en",
|
168
|
-
technologies:
|
199
|
+
technologies:
|
200
|
+
res.parsed.technologies.length > 0
|
201
|
+
? res.parsed.technologies
|
202
|
+
: ["education", "quizzes"],
|
169
203
|
},
|
170
204
|
})
|
171
205
|
|
@@ -206,6 +240,14 @@ function App() {
|
|
206
240
|
}
|
207
241
|
}
|
208
242
|
|
243
|
+
const checkTechs = async () => {
|
244
|
+
if (technologies.length === 0) {
|
245
|
+
const technologies = await getTechnologies()
|
246
|
+
console.log("TECHNOLOGIES", technologies)
|
247
|
+
setTechnologies(technologies)
|
248
|
+
}
|
249
|
+
}
|
250
|
+
|
209
251
|
const buildSteps = () => {
|
210
252
|
const steps = [
|
211
253
|
{
|
@@ -181,6 +181,9 @@ const FileUploader: React.FC<FileUploaderProps> = ({
|
|
181
181
|
const setUploadedFiles = useStore((state) => state.setUploadedFiles)
|
182
182
|
const [isDragging, setIsDragging] = useState(false)
|
183
183
|
const [isLoading, setIsLoading] = useState(false)
|
184
|
+
const [limitExceededBehavior, setLimitExceededBehavior] = useState<
|
185
|
+
"firstImages" | "cancel"
|
186
|
+
>("firstImages")
|
184
187
|
|
185
188
|
const extractTextFromFile = async (file: File): Promise<ParsedFile> => {
|
186
189
|
const { type, name } = file
|
@@ -203,6 +206,7 @@ const FileUploader: React.FC<FileUploaderProps> = ({
|
|
203
206
|
: window.location.origin
|
204
207
|
}/notifications/${resultId}`
|
205
208
|
formData.append("webhook_callback_url", webhookUrl)
|
209
|
+
formData.append("limit_exceeded_behavior", limitExceededBehavior)
|
206
210
|
|
207
211
|
const loadingToast = toast.loading(`Processing ${file.name}...`)
|
208
212
|
try {
|
@@ -226,9 +230,17 @@ const FileUploader: React.FC<FileUploaderProps> = ({
|
|
226
230
|
notificationId: resultId,
|
227
231
|
}
|
228
232
|
} catch (err: any) {
|
229
|
-
|
230
|
-
|
231
|
-
|
233
|
+
console.log(err.response.data, "ERROR IN FILE UPLOADER")
|
234
|
+
if (err.response.data.error_code === "too_many_images") {
|
235
|
+
toast.error(`❌ ${t("uploader.files.maxImagesPerPDFError")}`, {
|
236
|
+
id: loadingToast,
|
237
|
+
})
|
238
|
+
} else {
|
239
|
+
toast.error(t("uploader.files.errorProcessingPDF"), {
|
240
|
+
id: loadingToast,
|
241
|
+
})
|
242
|
+
}
|
243
|
+
|
232
244
|
return { name, text: "", status: "ERROR", notificationId: "" }
|
233
245
|
}
|
234
246
|
}
|
@@ -309,6 +321,26 @@ const FileUploader: React.FC<FileUploaderProps> = ({
|
|
309
321
|
onDrop={handleDrop}
|
310
322
|
className={isDragging ? "border-blue-600 bg-blue-50" : ""}
|
311
323
|
/>
|
324
|
+
<p className="text-xs text-gray-500">
|
325
|
+
{t("uploader.files.maxImagesPerPDFWarning")}
|
326
|
+
|
327
|
+
<select
|
328
|
+
className="bg-white text-black ml-2 p-1 rounded-md"
|
329
|
+
value={limitExceededBehavior}
|
330
|
+
onChange={(e) =>
|
331
|
+
setLimitExceededBehavior(
|
332
|
+
e.target.value as "firstImages" | "cancel"
|
333
|
+
)
|
334
|
+
}
|
335
|
+
>
|
336
|
+
<option value="firstImages">
|
337
|
+
{t("uploader.files.useOnlyFirstImages")}
|
338
|
+
</option>
|
339
|
+
<option value="cancel">
|
340
|
+
{t("uploader.files.cancelAndUploadAnother")}
|
341
|
+
</option>
|
342
|
+
</select>
|
343
|
+
</p>
|
312
344
|
<button
|
313
345
|
disabled={uploadedFiles.some((file) => file.status !== "SUCCESS")}
|
314
346
|
className="bg-blue-500 text-white px-4 py-2 rounded-md cursor-pointer disabled:opacity-50"
|