@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.
@@ -1,5 +1,6 @@
1
1
  import { IFile } from "../models/file"
2
- import API from "../utils/api"
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 ITelemetryManager {
109
- current: ITelemetryJSONSchema | null
110
+ interface ITelemetryManagerCLI {
110
111
  configPath: string | null
111
- user: TUser
112
- urls: TTelemetryUrls
113
- started: boolean
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
- streamEvent: (stepPosition: number, event: string, data: any) => void
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: ITelemetryManager = {
137
- current: null,
138
- urls: {},
139
- user: {
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 (agent, steps, path, tutorialSlug) {
130
+ start: function (path, socket) {
151
131
  this.configPath = path
152
- if (!this.current) {
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} for user ${this.user.id}`)
241
-
242
- if (!this.current) {
243
- // throw new Error("Telemetry has not been started");
244
- return
245
- }
246
-
247
- const step = this.current.steps[stepPosition]
248
- if (!step) {
249
- return
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 (error) {
354
- reject(error)
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
- getStep: function (stepPosition: number) {
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(this.current),
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
@@ -8,5 +8,6 @@ export type TAction =
8
8
  | "file_change"
9
9
  | "dialog"
10
10
  | "session-refreshed"
11
+ | "telemetry_event"
11
12
 
12
13
  export type ICallback = (...agrs: any[]) => any
@@ -13,4 +13,4 @@ export type TStatus =
13
13
  | "open_files"
14
14
  | "open_window"
15
15
  | "instructions_closed"
16
- | "completed";
16
+ | "completed"