@learnpack/learnpack 5.0.31 → 5.0.33
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/start.js +3 -27
- package/lib/managers/server/routes.js +9 -10
- package/lib/managers/session.js +0 -6
- package/lib/managers/telemetry.d.ts +8 -25
- package/lib/managers/telemetry.js +16 -207
- package/lib/models/action.d.ts +1 -1
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/src/commands/start.ts +2 -33
- package/src/managers/server/routes.ts +10 -11
- package/src/managers/session.ts +0 -5
- package/src/managers/socket.ts +274 -274
- package/src/managers/telemetry.ts +31 -271
- package/src/models/action.ts +1 -0
- package/src/models/status.ts +1 -1
@@ -1,5 +1,6 @@
|
|
1
1
|
import { IFile } from "../models/file"
|
2
|
-
import
|
2
|
+
import { ISocket } from "../models/socket"
|
3
|
+
const packageInfo = require("../../package.json")
|
3
4
|
import Console from "../utils/console"
|
4
5
|
|
5
6
|
const fs = require("fs")
|
@@ -77,6 +78,7 @@ type TStudent = {
|
|
77
78
|
|
78
79
|
export interface ITelemetryJSONSchema {
|
79
80
|
telemetry_id?: string
|
81
|
+
version: string
|
80
82
|
user_id?: number | string
|
81
83
|
slug: string
|
82
84
|
agent?: string
|
@@ -105,234 +107,46 @@ type TUser = {
|
|
105
107
|
email: string
|
106
108
|
}
|
107
109
|
|
108
|
-
interface
|
109
|
-
current: ITelemetryJSONSchema | null
|
110
|
+
interface ITelemetryManagerCLI {
|
110
111
|
configPath: string | null
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
salute: (message: string) => void
|
115
|
-
start: (
|
116
|
-
agent: string,
|
117
|
-
steps: TStep[],
|
118
|
-
path: string,
|
119
|
-
tutorialSlug: string
|
120
|
-
) => void
|
121
|
-
prevStep?: number
|
112
|
+
socket: ISocket | null
|
113
|
+
version: string
|
114
|
+
start: (path: string, socket: ISocket) => void
|
122
115
|
registerStepEvent: (
|
123
116
|
stepPosition: number,
|
124
117
|
event: TStepEvent,
|
125
118
|
data: any
|
126
119
|
) => void
|
127
|
-
|
128
|
-
submit: () => Promise<void>
|
129
|
-
finishWorkoutSession: () => void
|
130
|
-
setStudent: (student: TStudent) => void
|
131
|
-
save: () => void
|
120
|
+
save: (telemetry: ITelemetryJSONSchema) => void
|
132
121
|
retrieve: () => Promise<ITelemetryJSONSchema | null>
|
133
|
-
getStep: (stepPosition: number) => TStep | null
|
134
122
|
}
|
135
123
|
|
136
|
-
const TelemetryManager:
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
token: "",
|
141
|
-
id: "",
|
142
|
-
email: "",
|
143
|
-
},
|
124
|
+
const TelemetryManager: ITelemetryManagerCLI = {
|
125
|
+
version: packageInfo.version,
|
126
|
+
socket: null,
|
127
|
+
|
144
128
|
configPath: "",
|
145
|
-
started: false,
|
146
|
-
salute: message => {
|
147
|
-
Console.info(message)
|
148
|
-
},
|
149
129
|
|
150
|
-
start: function (
|
130
|
+
start: function (path, socket) {
|
151
131
|
this.configPath = path
|
152
|
-
|
153
|
-
this.retrieve()
|
154
|
-
.then(data => {
|
155
|
-
const prevTelemetry = data
|
156
|
-
if (prevTelemetry) {
|
157
|
-
this.current = prevTelemetry
|
158
|
-
this.finishWorkoutSession()
|
159
|
-
} else {
|
160
|
-
this.current = {
|
161
|
-
telemetry_id: createUUID(),
|
162
|
-
slug: tutorialSlug,
|
163
|
-
agent,
|
164
|
-
tutorial_started_at: Date.now(),
|
165
|
-
last_interaction_at: Date.now(),
|
166
|
-
steps,
|
167
|
-
workout_session: [
|
168
|
-
{
|
169
|
-
started_at: Date.now(),
|
170
|
-
},
|
171
|
-
],
|
172
|
-
}
|
173
|
-
}
|
174
|
-
|
175
|
-
if (this.current.user_id) {
|
176
|
-
this.user.id = this.current.user_id.toString()
|
177
|
-
}
|
178
|
-
|
179
|
-
this.save()
|
180
|
-
|
181
|
-
this.started = true
|
182
|
-
Console.debug("Telemetry started successfully!")
|
183
|
-
|
184
|
-
if (!this.user.id) {
|
185
|
-
Console.debug(
|
186
|
-
"No user ID found, impossible to submit telemetry at start"
|
187
|
-
)
|
188
|
-
return
|
189
|
-
}
|
190
|
-
|
191
|
-
this.submit()
|
192
|
-
})
|
193
|
-
.catch(error => {
|
194
|
-
Console.debug(error)
|
195
|
-
// Delete the telemetry.json if it exists
|
196
|
-
fs.unlinkSync(`${this.configPath}/telemetry.json`)
|
197
|
-
throw new Error(
|
198
|
-
"There was a problem starting, reload LearnPack\nRun\n$ learnpack start"
|
199
|
-
)
|
200
|
-
})
|
201
|
-
}
|
202
|
-
},
|
203
|
-
|
204
|
-
setStudent: function (student) {
|
205
|
-
if (!this.current) {
|
206
|
-
Console.debug("Telemetry has not been started")
|
207
|
-
|
208
|
-
return
|
209
|
-
}
|
210
|
-
|
211
|
-
Console.debug("Setting student", student)
|
212
|
-
|
213
|
-
this.current.user_id = student.user_id
|
214
|
-
this.user.id = student.user_id
|
215
|
-
this.user.token = student.token
|
216
|
-
this.user.email = student.email
|
217
|
-
this.save()
|
218
|
-
this.submit()
|
219
|
-
},
|
220
|
-
finishWorkoutSession: function () {
|
221
|
-
if (!this.current) {
|
222
|
-
return
|
223
|
-
}
|
224
|
-
|
225
|
-
const lastSession =
|
226
|
-
this.current?.workout_session[this.current.workout_session.length - 1]
|
227
|
-
if (
|
228
|
-
lastSession &&
|
229
|
-
!lastSession.ended_at &&
|
230
|
-
this.current?.last_interaction_at
|
231
|
-
) {
|
232
|
-
lastSession.ended_at = this.current.last_interaction_at
|
233
|
-
this.current.workout_session.push({
|
234
|
-
started_at: Date.now(),
|
235
|
-
})
|
236
|
-
}
|
132
|
+
this.socket = socket
|
237
133
|
},
|
238
134
|
|
239
135
|
registerStepEvent: function (stepPosition, event, data) {
|
240
|
-
Console.debug(`Registering Event ${event}
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
if (data.source_code) {
|
253
|
-
data.source_code = stringToBase64(data.source_code)
|
254
|
-
}
|
255
|
-
|
256
|
-
if (data.stdout) {
|
257
|
-
data.stdout = stringToBase64(data.stdout)
|
258
|
-
}
|
259
|
-
|
260
|
-
if (data.stderr) {
|
261
|
-
data.stderr = stringToBase64(data.stderr)
|
262
|
-
}
|
263
|
-
|
264
|
-
if (Object.prototype.hasOwnProperty.call(data, "exitCode")) {
|
265
|
-
data.exit_code = data.exitCode
|
266
|
-
data.exitCode = undefined
|
267
|
-
}
|
268
|
-
|
269
|
-
switch (event) {
|
270
|
-
case "compile":
|
271
|
-
if (!step.compilations) {
|
272
|
-
step.compilations = []
|
273
|
-
}
|
274
|
-
|
275
|
-
step.compilations.push(data)
|
276
|
-
this.current.steps[stepPosition] = step
|
277
|
-
break
|
278
|
-
case "test":
|
279
|
-
if (!step.tests) {
|
280
|
-
step.tests = []
|
281
|
-
}
|
282
|
-
|
283
|
-
// data.stdout =
|
284
|
-
step.tests.push(data)
|
285
|
-
if (data.exit_code === 0) {
|
286
|
-
step.completed_at = Date.now()
|
287
|
-
}
|
288
|
-
|
289
|
-
this.current.steps[stepPosition] = step
|
290
|
-
break
|
291
|
-
case "ai_interaction":
|
292
|
-
if (!step.ai_interactions) {
|
293
|
-
step.ai_interactions = []
|
294
|
-
}
|
295
|
-
|
296
|
-
step.ai_interactions.push(data)
|
297
|
-
break
|
298
|
-
|
299
|
-
case "quiz_submission": {
|
300
|
-
if (!step.quiz_submissions) {
|
301
|
-
step.quiz_submissions = []
|
302
|
-
}
|
303
|
-
|
304
|
-
step.quiz_submissions.push(data)
|
305
|
-
break
|
136
|
+
Console.debug(`Registering Telemetry Event ${event}`)
|
137
|
+
|
138
|
+
this.socket?.emit(
|
139
|
+
"telemetry_event",
|
140
|
+
"ready",
|
141
|
+
[`Registering telemetry event ${event}`],
|
142
|
+
[],
|
143
|
+
[],
|
144
|
+
{
|
145
|
+
stepPosition,
|
146
|
+
event,
|
147
|
+
data,
|
306
148
|
}
|
307
|
-
|
308
|
-
case "open_step": {
|
309
|
-
const now = Date.now()
|
310
|
-
|
311
|
-
if (!step.opened_at) {
|
312
|
-
step.opened_at = now
|
313
|
-
this.current.steps[stepPosition] = step
|
314
|
-
}
|
315
|
-
|
316
|
-
if (this.prevStep || this.prevStep === 0) {
|
317
|
-
const prevStep = this.current.steps[this.prevStep]
|
318
|
-
if (!prevStep.is_testeable && !prevStep.completed_at) {
|
319
|
-
prevStep.completed_at = now
|
320
|
-
this.current.steps[this.prevStep] = prevStep
|
321
|
-
}
|
322
|
-
}
|
323
|
-
|
324
|
-
this.prevStep = stepPosition
|
325
|
-
this.submit()
|
326
|
-
break
|
327
|
-
}
|
328
|
-
|
329
|
-
default:
|
330
|
-
throw new Error(`Event type ${event} is not supported`)
|
331
|
-
}
|
332
|
-
|
333
|
-
this.current.last_interaction_at = Date.now()
|
334
|
-
this.streamEvent(stepPosition, event, data)
|
335
|
-
this.save()
|
149
|
+
)
|
336
150
|
},
|
337
151
|
retrieve: function () {
|
338
152
|
return new Promise((resolve, reject) => {
|
@@ -350,8 +164,8 @@ const TelemetryManager: ITelemetryManager = {
|
|
350
164
|
} else {
|
351
165
|
try {
|
352
166
|
resolve(JSON.parse(data))
|
353
|
-
} catch
|
354
|
-
|
167
|
+
} catch {
|
168
|
+
resolve(null)
|
355
169
|
}
|
356
170
|
}
|
357
171
|
}
|
@@ -359,70 +173,16 @@ const TelemetryManager: ITelemetryManager = {
|
|
359
173
|
})
|
360
174
|
},
|
361
175
|
|
362
|
-
|
363
|
-
return this.current?.steps[stepPosition] || null
|
364
|
-
},
|
365
|
-
|
366
|
-
submit: async function () {
|
367
|
-
Console.debug("Submitting telemetry...")
|
368
|
-
|
369
|
-
if (!this.current) {
|
370
|
-
Console.debug("Telemetry has not been started")
|
371
|
-
return Promise.resolve()
|
372
|
-
}
|
373
|
-
|
374
|
-
if (!this.user.id) {
|
375
|
-
Console.debug("User ID not found, skipping batch telemetry delivery")
|
376
|
-
return Promise.resolve()
|
377
|
-
}
|
378
|
-
|
379
|
-
const url = this.urls.batch
|
380
|
-
if (!url) {
|
381
|
-
Console.debug("Batch URL not found, skipping batch telemetry delivery")
|
382
|
-
return Promise.resolve()
|
383
|
-
}
|
384
|
-
|
385
|
-
const body = this.current
|
386
|
-
|
387
|
-
if (!body.user_id) {
|
388
|
-
body.user_id = this.user.id
|
389
|
-
}
|
390
|
-
|
391
|
-
API.sendBatchTelemetry(url, body)
|
392
|
-
},
|
393
|
-
save: function () {
|
176
|
+
save: function (telemetry: ITelemetryJSONSchema) {
|
394
177
|
fs.writeFile(
|
395
178
|
`${this.configPath}/telemetry.json`,
|
396
|
-
JSON.stringify(
|
179
|
+
JSON.stringify(telemetry),
|
397
180
|
(err: any) => {
|
398
181
|
if (err)
|
399
182
|
throw err
|
400
183
|
}
|
401
184
|
)
|
402
185
|
},
|
403
|
-
|
404
|
-
streamEvent: async function (stepPosition, event, data) {
|
405
|
-
if (!this.current)
|
406
|
-
return
|
407
|
-
|
408
|
-
const url = this.urls.streaming
|
409
|
-
if (!url) {
|
410
|
-
return
|
411
|
-
}
|
412
|
-
|
413
|
-
const stepSlug = this.current.steps[stepPosition].slug
|
414
|
-
|
415
|
-
const body = {
|
416
|
-
slug: stepSlug,
|
417
|
-
telemetry_id: this.current.telemetry_id,
|
418
|
-
user_id: this.current.user_id,
|
419
|
-
step_position: stepPosition,
|
420
|
-
event,
|
421
|
-
data,
|
422
|
-
}
|
423
|
-
|
424
|
-
API.sendStreamTelemetry(url, body)
|
425
|
-
},
|
426
186
|
}
|
427
187
|
|
428
188
|
export default TelemetryManager
|
package/src/models/action.ts
CHANGED
package/src/models/status.ts
CHANGED