@learnpack/learnpack 5.0.66 → 5.0.68
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 +13 -13
- package/lib/commands/serve.js +15 -15
- package/lib/creatorDist/assets/index-B01XTAAq.js +75129 -0
- package/{src/creatorDist/assets/index-tt9JBVY0.css → lib/creatorDist/assets/index-t6ma_gVm.css} +118 -20
- package/lib/creatorDist/assets/pdf.worker-DSVOJ9H9.js +56037 -0
- package/lib/creatorDist/index.html +10 -5
- package/lib/creatorDist/logo-192 copy.png +0 -0
- package/lib/creatorDist/logo.png +0 -0
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/src/commands/serve.ts +24 -22
- package/src/creator/index.html +8 -3
- package/src/creator/package-lock.json +394 -0
- package/src/creator/package.json +3 -0
- package/src/creator/public/logo-192 copy.png +0 -0
- package/src/creator/public/logo.png +0 -0
- package/src/creator/src/App.tsx +30 -4
- package/src/creator/src/assets/svgs.tsx +138 -0
- package/src/creator/src/components/ConsumablesManager.tsx +21 -0
- package/src/creator/src/components/FileUploader.tsx +91 -0
- package/src/creator/src/components/LessonItem.tsx +70 -0
- package/src/creator/src/components/Loader.tsx +64 -19
- package/src/creator/src/components/Login.tsx +6 -12
- package/src/creator/src/components/Message.tsx +28 -0
- package/src/creator/src/components/RigoLoader.tsx +14 -0
- package/src/creator/src/components/StepWizard.tsx +1 -0
- package/src/creator/src/components/SyllabusEditor.tsx +135 -266
- package/src/creator/src/index.css +64 -0
- package/src/creator/src/utils/creatorUtils.ts +136 -0
- package/src/creator/src/utils/eventBus.ts +2 -0
- package/src/creator/src/utils/lib.ts +86 -0
- package/src/creator/src/utils/store.ts +25 -1
- package/src/creatorDist/assets/index-B01XTAAq.js +75129 -0
- package/{lib/creatorDist/assets/index-tt9JBVY0.css → src/creatorDist/assets/index-t6ma_gVm.css} +118 -20
- package/src/creatorDist/assets/pdf.worker-DSVOJ9H9.js +56037 -0
- package/src/creatorDist/index.html +10 -5
- package/src/creatorDist/logo-192 copy.png +0 -0
- package/src/creatorDist/logo.png +0 -0
- package/src/ui/_app/app.css +1 -0
- package/src/ui/_app/app.js +3027 -0
- package/src/ui/_app/favicon.ico +0 -0
- package/src/ui/_app/index.html +109 -0
- package/src/ui/_app/index.html.backup +91 -0
- package/src/ui/_app/learnpack.svg +7 -0
- package/src/ui/_app/logo-192.png +0 -0
- package/src/ui/_app/logo-512.png +0 -0
- package/src/ui/_app/logo.png +0 -0
- package/src/ui/_app/manifest.webmanifest +21 -0
- package/src/ui/_app/sw.js +30 -0
- package/src/ui/app.tar.gz +0 -0
- package/lib/creatorDist/assets/index-CrrS9sA3.js +0 -23718
- package/src/creator/src/App.css +0 -42
- package/src/creatorDist/assets/index-CrrS9sA3.js +0 -23718
@@ -0,0 +1,136 @@
|
|
1
|
+
import { Lesson } from "../components/LessonItem"
|
2
|
+
import { eventBus } from "./eventBus"
|
3
|
+
import { uploadFileToBucket } from "./lib"
|
4
|
+
import { makeReadmeReadable, readmeCreator, checkReadability } from "./rigo"
|
5
|
+
import { FormState } from "./store"
|
6
|
+
|
7
|
+
export const slugify = (text: string) => {
|
8
|
+
return text
|
9
|
+
.toLowerCase()
|
10
|
+
.replace(/ /g, "-")
|
11
|
+
.replace(/[^\w.-]+/g, "")
|
12
|
+
}
|
13
|
+
|
14
|
+
export const createLearnJson = (courseInfo: FormState) => {
|
15
|
+
const learnJson = {
|
16
|
+
slug: slugify(courseInfo.title || randomUUID()),
|
17
|
+
title: {
|
18
|
+
us: courseInfo.title,
|
19
|
+
},
|
20
|
+
description: {
|
21
|
+
us: courseInfo.description,
|
22
|
+
},
|
23
|
+
grading: "isolated",
|
24
|
+
}
|
25
|
+
return learnJson
|
26
|
+
}
|
27
|
+
|
28
|
+
const PARAMS = {
|
29
|
+
expected_grade_level: "6",
|
30
|
+
max_fkgl: 8,
|
31
|
+
max_words: 200,
|
32
|
+
max_rewrite_attempts: 3,
|
33
|
+
max_title_length: 50,
|
34
|
+
}
|
35
|
+
export async function processExercise(
|
36
|
+
rigoToken: string,
|
37
|
+
steps: Lesson[],
|
38
|
+
packageContext: string,
|
39
|
+
exercise: Lesson,
|
40
|
+
exercisesDir: string
|
41
|
+
): Promise<string> {
|
42
|
+
// const tid = toast.loading("Generating lesson...")
|
43
|
+
setTimeout(() => {
|
44
|
+
eventBus.emit("course-generation", {
|
45
|
+
message: `✍🏻 Generating lesson ${exercise.id} - ${exercise.title}...`,
|
46
|
+
})
|
47
|
+
}, 500)
|
48
|
+
const readme = await readmeCreator(rigoToken, {
|
49
|
+
title: `${exercise.id} - ${exercise.title}`,
|
50
|
+
output_lang: "en",
|
51
|
+
list_of_exercises: JSON.stringify(steps),
|
52
|
+
tutorial_description: packageContext,
|
53
|
+
lesson_description: exercise.description,
|
54
|
+
kind: exercise.type.toLowerCase(),
|
55
|
+
})
|
56
|
+
|
57
|
+
const duration = exercise.duration
|
58
|
+
let attempts = 0
|
59
|
+
let readability = checkReadability(readme.parsed.content, 200, duration || 1)
|
60
|
+
|
61
|
+
while (
|
62
|
+
readability.fkglResult.fkgl > PARAMS.max_fkgl &&
|
63
|
+
attempts < PARAMS.max_rewrite_attempts
|
64
|
+
) {
|
65
|
+
setTimeout(() => {
|
66
|
+
eventBus.emit("course-generation", {
|
67
|
+
message: `🔄 The lesson ${exercise.id} - ${
|
68
|
+
exercise.title
|
69
|
+
} has a readability score of ${
|
70
|
+
readability.fkglResult.fkgl
|
71
|
+
}. Rewriting it... (Attempt ${attempts + 1})`,
|
72
|
+
})
|
73
|
+
}, 500)
|
74
|
+
// eslint-disable-next-line
|
75
|
+
const reducedReadme = await makeReadmeReadable(rigoToken, {
|
76
|
+
lesson: readability.body,
|
77
|
+
number_of_words: readability.minutes.toString(),
|
78
|
+
expected_number_words: PARAMS.max_words.toString(),
|
79
|
+
fkgl_results: JSON.stringify(readability.fkglResult),
|
80
|
+
expected_grade_level: PARAMS.expected_grade_level,
|
81
|
+
})
|
82
|
+
|
83
|
+
// console.log("REDUCED README START", reducedReadme, "REDUCED README END")
|
84
|
+
|
85
|
+
if (!reducedReadme) break
|
86
|
+
|
87
|
+
readability = checkReadability(
|
88
|
+
reducedReadme.parsed.content,
|
89
|
+
PARAMS.max_words,
|
90
|
+
duration || 1
|
91
|
+
)
|
92
|
+
|
93
|
+
attempts++
|
94
|
+
}
|
95
|
+
|
96
|
+
setTimeout(() => {
|
97
|
+
eventBus.emit("course-generation", {
|
98
|
+
message: `✅ After ${attempts} attempts, the lesson ${
|
99
|
+
exercise.title
|
100
|
+
} has a readability score of ${
|
101
|
+
readability.fkglResult.fkgl
|
102
|
+
} using FKGL. And it has ${readability.minutes.toFixed(
|
103
|
+
2
|
104
|
+
)} minutes of reading time.`,
|
105
|
+
})
|
106
|
+
}, 500)
|
107
|
+
|
108
|
+
const readmeFilename = "README.md"
|
109
|
+
await uploadFileToBucket(
|
110
|
+
readability.newMarkdown,
|
111
|
+
`${exercisesDir}/${slugify(
|
112
|
+
exercise.id + "-" + exercise.title
|
113
|
+
)}/${readmeFilename}`
|
114
|
+
)
|
115
|
+
|
116
|
+
if (exercise.type.toLowerCase() === "code") {
|
117
|
+
// const codeFile = await createCodeFile(rigoToken, {
|
118
|
+
// readme: readability.newMarkdown,
|
119
|
+
// tutorial_info: packageContext,
|
120
|
+
// })
|
121
|
+
// fs.writeFileSync(
|
122
|
+
// path.join(
|
123
|
+
// exerciseDir,
|
124
|
+
// `app.${codeFile.parsed.extension.replace(".", "")}`
|
125
|
+
// ),
|
126
|
+
// codeFile.parsed.content
|
127
|
+
// )
|
128
|
+
}
|
129
|
+
|
130
|
+
// toast.success("Lesson generated successfully", { id: tid })
|
131
|
+
return readability.newMarkdown
|
132
|
+
}
|
133
|
+
|
134
|
+
export const randomUUID = () => {
|
135
|
+
return Math.random().toString(36).substring(2, 15)
|
136
|
+
}
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import axios from "axios"
|
2
|
+
import { BREATHECODE_HOST } from "./constants"
|
2
3
|
|
3
4
|
type ParsedLesson = {
|
4
5
|
id: string
|
@@ -34,3 +35,88 @@ export const uploadFileToBucket = async (content: string, path: string) => {
|
|
34
35
|
})
|
35
36
|
return response.data
|
36
37
|
}
|
38
|
+
|
39
|
+
export const checkParams = () => {
|
40
|
+
const urlParams = new URLSearchParams(window.location.search)
|
41
|
+
const token = urlParams.get("token")
|
42
|
+
return { token }
|
43
|
+
}
|
44
|
+
|
45
|
+
export async function getConsumables(token: string): Promise<any> {
|
46
|
+
const url = `${BREATHECODE_HOST}/v1/payments/me/service/consumable?virtual=true`
|
47
|
+
|
48
|
+
const headers = {
|
49
|
+
Authorization: `Token ${token}`,
|
50
|
+
}
|
51
|
+
|
52
|
+
try {
|
53
|
+
const response = await axios.get(url, { headers })
|
54
|
+
return response.data
|
55
|
+
} catch (error) {
|
56
|
+
console.error("Error fetching consumables:", error)
|
57
|
+
throw error
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
type ConsumableSlug =
|
62
|
+
| "ai-conversation-message"
|
63
|
+
| "ai-compilation"
|
64
|
+
| "ai-generation"
|
65
|
+
| "ai-course-generation"
|
66
|
+
|
67
|
+
export async function useConsumableCall(
|
68
|
+
breathecodeToken: string,
|
69
|
+
consumableSlug: ConsumableSlug = "ai-conversation-message"
|
70
|
+
): Promise<boolean> {
|
71
|
+
const url = `${BREATHECODE_HOST}/v1/payments/me/service/${consumableSlug}/consumptionsession`
|
72
|
+
|
73
|
+
const headers = {
|
74
|
+
Authorization: `Token ${breathecodeToken}`,
|
75
|
+
}
|
76
|
+
|
77
|
+
try {
|
78
|
+
const response = await axios.put(url, {}, { headers })
|
79
|
+
|
80
|
+
if (response.status >= 200 && response.status < 300) {
|
81
|
+
console.log(response.data)
|
82
|
+
console.log(`Successfully consumed ${consumableSlug}`)
|
83
|
+
return true
|
84
|
+
} else {
|
85
|
+
console.error(`Request failed with status code: ${response.status}`)
|
86
|
+
console.error(`Response: ${response.data}`)
|
87
|
+
return false
|
88
|
+
}
|
89
|
+
} catch (error) {
|
90
|
+
console.error(`Error consuming ${consumableSlug}:`, error)
|
91
|
+
return false
|
92
|
+
}
|
93
|
+
}
|
94
|
+
|
95
|
+
type ConsumableItem = {
|
96
|
+
id: number
|
97
|
+
how_many: number
|
98
|
+
unit_type: string
|
99
|
+
valid_until: string | null
|
100
|
+
}
|
101
|
+
|
102
|
+
type VoidEntry = {
|
103
|
+
id: number
|
104
|
+
slug: string
|
105
|
+
balance: { unit: number }
|
106
|
+
items: ConsumableItem[]
|
107
|
+
}
|
108
|
+
|
109
|
+
export const parseConsumables = (
|
110
|
+
voids: VoidEntry[]
|
111
|
+
): Record<string, number> => {
|
112
|
+
const result: Record<string, number> = {}
|
113
|
+
|
114
|
+
voids.forEach((entry) => {
|
115
|
+
const maxHowMany = entry.items.length
|
116
|
+
? Math.max(...entry.items.map((item) => item.how_many))
|
117
|
+
: 0
|
118
|
+
result[entry.slug] = maxHowMany
|
119
|
+
})
|
120
|
+
|
121
|
+
return result
|
122
|
+
}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { create } from "zustand"
|
2
2
|
import { persist } from "zustand/middleware"
|
3
|
-
import { Lesson } from "../components/
|
3
|
+
import { Lesson } from "../components/LessonItem"
|
4
4
|
|
5
5
|
export type FormState = {
|
6
6
|
description: string
|
@@ -21,6 +21,14 @@ type Auth = {
|
|
21
21
|
type Syllabus = {
|
22
22
|
lessons: Lesson[]
|
23
23
|
courseInfo: FormState
|
24
|
+
uploadedFiles: {
|
25
|
+
name: string
|
26
|
+
text: string
|
27
|
+
}[]
|
28
|
+
}
|
29
|
+
|
30
|
+
type Consumables = {
|
31
|
+
[key: string]: number
|
24
32
|
}
|
25
33
|
|
26
34
|
type Store = {
|
@@ -30,6 +38,8 @@ type Store = {
|
|
30
38
|
syllabus: Syllabus
|
31
39
|
setSyllabus: (syllabus: Partial<Syllabus>) => void
|
32
40
|
setFormState: (formState: Partial<FormState>) => void
|
41
|
+
consumables: Consumables
|
42
|
+
setConsumables: (consumables: Partial<Consumables>) => void
|
33
43
|
}
|
34
44
|
|
35
45
|
const useStore = create<Store>()(
|
@@ -64,7 +74,21 @@ const useStore = create<Store>()(
|
|
64
74
|
currentStep: 0,
|
65
75
|
title: "",
|
66
76
|
},
|
77
|
+
uploadedFiles: [],
|
67
78
|
},
|
79
|
+
consumables: {},
|
80
|
+
setConsumables: (consumables: Partial<Consumables>) =>
|
81
|
+
set((state) => {
|
82
|
+
const sanitized: Consumables = Object.fromEntries(
|
83
|
+
Object.entries(consumables).map(([k, v]) => [k, v ?? 0])
|
84
|
+
)
|
85
|
+
return {
|
86
|
+
consumables: {
|
87
|
+
...state.consumables,
|
88
|
+
...sanitized,
|
89
|
+
},
|
90
|
+
}
|
91
|
+
}),
|
68
92
|
setSyllabus: (syllabus: Partial<Syllabus>) =>
|
69
93
|
set((state) => ({ syllabus: { ...state.syllabus, ...syllabus } })),
|
70
94
|
setAuth: (auth: Auth) => set({ auth }),
|