@learnpack/learnpack 5.0.11 → 5.0.13
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 +11 -11
- package/lib/commands/start.js +5 -0
- package/lib/managers/server/routes.js +5 -0
- package/lib/managers/session.js +6 -1
- package/lib/managers/telemetry.d.ts +21 -3
- package/lib/managers/telemetry.js +41 -9
- package/lib/models/session.d.ts +2 -0
- package/lib/utils/api.d.ts +1 -1
- package/lib/utils/api.js +7 -2
- package/lib/utils/checkNotInstalled.js +1 -0
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/src/commands/start.ts +6 -0
- package/src/managers/server/routes.ts +6 -0
- package/src/managers/session.ts +9 -1
- package/src/managers/telemetry.ts +423 -353
- package/src/models/session.ts +2 -0
- package/src/utils/api.ts +10 -3
- package/src/utils/checkNotInstalled.ts +2 -0
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.13 win32-x64 node-v20.16.0
|
25
25
|
$ learnpack --help [COMMAND]
|
26
26
|
USAGE
|
27
27
|
$ learnpack COMMAND
|
@@ -75,7 +75,7 @@ DESCRIPTION
|
|
75
75
|
12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)
|
76
76
|
```
|
77
77
|
|
78
|
-
_See code: [src\commands\audit.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
78
|
+
_See code: [src\commands\audit.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.13/src\commands\audit.ts)_
|
79
79
|
|
80
80
|
## `learnpack clean`
|
81
81
|
|
@@ -90,7 +90,7 @@ DESCRIPTION
|
|
90
90
|
Extra documentation goes here
|
91
91
|
```
|
92
92
|
|
93
|
-
_See code: [src\commands\clean.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
93
|
+
_See code: [src\commands\clean.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.13/src\commands\clean.ts)_
|
94
94
|
|
95
95
|
## `learnpack download [PACKAGE]`
|
96
96
|
|
@@ -108,7 +108,7 @@ DESCRIPTION
|
|
108
108
|
Extra documentation goes here
|
109
109
|
```
|
110
110
|
|
111
|
-
_See code: [src\commands\download.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
111
|
+
_See code: [src\commands\download.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.13/src\commands\download.ts)_
|
112
112
|
|
113
113
|
## `learnpack help [COMMAND]`
|
114
114
|
|
@@ -139,7 +139,7 @@ OPTIONS
|
|
139
139
|
-h, --grading show CLI help
|
140
140
|
```
|
141
141
|
|
142
|
-
_See code: [src\commands\init.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
142
|
+
_See code: [src\commands\init.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.13/src\commands\init.ts)_
|
143
143
|
|
144
144
|
## `learnpack login [PACKAGE]`
|
145
145
|
|
@@ -157,7 +157,7 @@ DESCRIPTION
|
|
157
157
|
Extra documentation goes here
|
158
158
|
```
|
159
159
|
|
160
|
-
_See code: [src\commands\login.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
160
|
+
_See code: [src\commands\login.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.13/src\commands\login.ts)_
|
161
161
|
|
162
162
|
## `learnpack logout [PACKAGE]`
|
163
163
|
|
@@ -175,7 +175,7 @@ DESCRIPTION
|
|
175
175
|
Extra documentation goes here
|
176
176
|
```
|
177
177
|
|
178
|
-
_See code: [src\commands\logout.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
178
|
+
_See code: [src\commands\logout.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.13/src\commands\logout.ts)_
|
179
179
|
|
180
180
|
## `learnpack plugins`
|
181
181
|
|
@@ -306,7 +306,7 @@ OPTIONS
|
|
306
306
|
-h, --help show CLI help
|
307
307
|
```
|
308
308
|
|
309
|
-
_See code: [src\commands\publish.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
309
|
+
_See code: [src\commands\publish.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.13/src\commands\publish.ts)_
|
310
310
|
|
311
311
|
## `learnpack start`
|
312
312
|
|
@@ -327,7 +327,7 @@ OPTIONS
|
|
327
327
|
-w, --watch Watch for file changes
|
328
328
|
```
|
329
329
|
|
330
|
-
_See code: [src\commands\start.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
330
|
+
_See code: [src\commands\start.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.13/src\commands\start.ts)_
|
331
331
|
|
332
332
|
## `learnpack test [EXERCISESLUG]`
|
333
333
|
|
@@ -341,7 +341,7 @@ ARGUMENTS
|
|
341
341
|
EXERCISESLUG The name of the exercise to test
|
342
342
|
```
|
343
343
|
|
344
|
-
_See code: [src\commands\test.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
344
|
+
_See code: [src\commands\test.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.13/src\commands\test.ts)_
|
345
345
|
|
346
346
|
## `learnpack translate`
|
347
347
|
|
@@ -352,7 +352,7 @@ USAGE
|
|
352
352
|
$ learnpack translate
|
353
353
|
```
|
354
354
|
|
355
|
-
_See code: [src\commands\translate.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.
|
355
|
+
_See code: [src\commands\translate.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.13/src\commands\translate.ts)_
|
356
356
|
<!-- commandsstop -->
|
357
357
|
|
358
358
|
> > > > > > > 0cb3e56d84c197f9d008836bb573eade212b7e57
|
package/lib/commands/start.js
CHANGED
@@ -69,6 +69,7 @@ class StartCommand extends SessionCommand_1.default {
|
|
69
69
|
compilations: [],
|
70
70
|
tests: [],
|
71
71
|
is_testeable: e.graded || false,
|
72
|
+
quiz_submissions: [],
|
72
73
|
}));
|
73
74
|
if (path && steps.length > 0) {
|
74
75
|
telemetry_1.default.start(agent, steps, path, tutorialSlug);
|
@@ -141,6 +142,10 @@ class StartCommand extends SessionCommand_1.default {
|
|
141
142
|
telemetry: telemetry_1.default,
|
142
143
|
});
|
143
144
|
});
|
145
|
+
socket_1.default.on("quiz_submission", (data) => {
|
146
|
+
const { stepPosition, event, eventData } = data;
|
147
|
+
telemetry_1.default.registerStepEvent(stepPosition, event, eventData);
|
148
|
+
});
|
144
149
|
socket_1.default.on("ai_interaction", (data) => {
|
145
150
|
const { stepPosition, event, eventData } = data;
|
146
151
|
telemetry_1.default.registerStepEvent(stepPosition, event, eventData);
|
@@ -134,6 +134,11 @@ async function default_1(app, configObject, configManager) {
|
|
134
134
|
const payload = await session_1.default.getPayload();
|
135
135
|
if (payload && payload.rigobot && payload.rigobot.key) {
|
136
136
|
res.json({ rigoToken: payload.rigobot.key, payload: payload });
|
137
|
+
telemetry_1.default.setStudent({
|
138
|
+
user_id: payload.user_id,
|
139
|
+
email: payload.email,
|
140
|
+
token: payload.token,
|
141
|
+
});
|
137
142
|
}
|
138
143
|
else {
|
139
144
|
res
|
package/lib/managers/session.js
CHANGED
@@ -49,8 +49,13 @@ const Session = {
|
|
49
49
|
},
|
50
50
|
setPayload: async function (value) {
|
51
51
|
await this.initialize();
|
52
|
-
await storage.setItem("bc-payload", Object.assign({
|
52
|
+
await storage.setItem("bc-payload", Object.assign({}, value));
|
53
53
|
console_1.default.debug("Payload successfuly found and set for " + value.email);
|
54
|
+
// TelemetryManager.setStudent({
|
55
|
+
// user_id: value.user_id.toString(),
|
56
|
+
// email: value.email,
|
57
|
+
// token: value.token,
|
58
|
+
// })
|
54
59
|
return true;
|
55
60
|
},
|
56
61
|
getPayload: async function () {
|
@@ -20,6 +20,16 @@ type TAIInteraction = {
|
|
20
20
|
starting_at: number;
|
21
21
|
ending_at: number;
|
22
22
|
};
|
23
|
+
type TQuizSelection = {
|
24
|
+
question: string;
|
25
|
+
answer: string;
|
26
|
+
isCorrect: boolean;
|
27
|
+
};
|
28
|
+
type TQuizSubmission = {
|
29
|
+
quiz_hash: string;
|
30
|
+
selections: TQuizSelection[];
|
31
|
+
submitted_at: number;
|
32
|
+
};
|
23
33
|
export type TStep = {
|
24
34
|
slug: string;
|
25
35
|
position: number;
|
@@ -30,6 +40,7 @@ export type TStep = {
|
|
30
40
|
compilations: TCompilationAttempt[];
|
31
41
|
tests: TTestAttempt[];
|
32
42
|
ai_interactions: TAIInteraction[];
|
43
|
+
quiz_submissions: TQuizSubmission[];
|
33
44
|
};
|
34
45
|
type TWorkoutSession = {
|
35
46
|
started_at: number;
|
@@ -45,20 +56,27 @@ export interface ITelemetryJSONSchema {
|
|
45
56
|
user_id?: number | string;
|
46
57
|
slug: string;
|
47
58
|
agent?: string;
|
48
|
-
tutorial_started_at
|
49
|
-
last_interaction_at
|
59
|
+
tutorial_started_at: number;
|
60
|
+
last_interaction_at: number;
|
50
61
|
steps: Array<TStep>;
|
51
62
|
workout_session: TWorkoutSession[];
|
52
63
|
}
|
53
|
-
type TStepEvent = "compile" | "test" | "ai_interaction" | "open_step";
|
64
|
+
type TStepEvent = "compile" | "test" | "ai_interaction" | "open_step" | "quiz_submission";
|
54
65
|
export type TTelemetryUrls = {
|
55
66
|
streaming?: string;
|
56
67
|
batch?: string;
|
57
68
|
};
|
69
|
+
type TUser = {
|
70
|
+
token: string;
|
71
|
+
id: string;
|
72
|
+
email: string;
|
73
|
+
};
|
58
74
|
interface ITelemetryManager {
|
59
75
|
current: ITelemetryJSONSchema | null;
|
60
76
|
configPath: string | null;
|
77
|
+
user: TUser;
|
61
78
|
urls: TTelemetryUrls;
|
79
|
+
started: boolean;
|
62
80
|
salute: (message: string) => void;
|
63
81
|
start: (agent: string, steps: TStep[], path: string, tutorialSlug: string) => void;
|
64
82
|
prevStep?: number;
|
@@ -13,7 +13,13 @@ function stringToBase64(input) {
|
|
13
13
|
const TelemetryManager = {
|
14
14
|
current: null,
|
15
15
|
urls: {},
|
16
|
+
user: {
|
17
|
+
token: "",
|
18
|
+
id: "",
|
19
|
+
email: "",
|
20
|
+
},
|
16
21
|
configPath: "",
|
22
|
+
started: false,
|
17
23
|
salute: message => {
|
18
24
|
console_1.default.info(message);
|
19
25
|
},
|
@@ -33,6 +39,7 @@ const TelemetryManager = {
|
|
33
39
|
slug: tutorialSlug,
|
34
40
|
agent,
|
35
41
|
tutorial_started_at: Date.now(),
|
42
|
+
last_interaction_at: Date.now(),
|
36
43
|
steps,
|
37
44
|
workout_session: [
|
38
45
|
{
|
@@ -41,7 +48,16 @@ const TelemetryManager = {
|
|
41
48
|
],
|
42
49
|
};
|
43
50
|
}
|
51
|
+
if (this.current.user_id) {
|
52
|
+
this.user.id = this.current.user_id.toString();
|
53
|
+
}
|
44
54
|
this.save();
|
55
|
+
this.started = true;
|
56
|
+
console_1.default.debug("Telemetry started successfully!");
|
57
|
+
if (!this.user.id) {
|
58
|
+
console_1.default.debug("No user ID found, impossible to submit telemetry at start");
|
59
|
+
return;
|
60
|
+
}
|
45
61
|
this.submit();
|
46
62
|
})
|
47
63
|
.catch(error => {
|
@@ -52,18 +68,16 @@ const TelemetryManager = {
|
|
52
68
|
});
|
53
69
|
}
|
54
70
|
},
|
55
|
-
// verifyStudent: function () {
|
56
|
-
// if (!this.current) {
|
57
|
-
// return;
|
58
|
-
// }
|
59
|
-
// if (!this.current.user_id) {
|
60
|
-
// }
|
61
|
-
// },
|
62
71
|
setStudent: function (student) {
|
63
72
|
if (!this.current) {
|
73
|
+
console_1.default.debug("Telemetry has not been started");
|
64
74
|
return;
|
65
75
|
}
|
76
|
+
console_1.default.debug("Setting student", student);
|
66
77
|
this.current.user_id = student.user_id;
|
78
|
+
this.user.id = student.user_id;
|
79
|
+
this.user.token = student.token;
|
80
|
+
this.user.email = student.email;
|
67
81
|
this.save();
|
68
82
|
this.submit();
|
69
83
|
},
|
@@ -83,6 +97,7 @@ const TelemetryManager = {
|
|
83
97
|
}
|
84
98
|
},
|
85
99
|
registerStepEvent: function (stepPosition, event, data) {
|
100
|
+
console_1.default.debug(`Registering Event ${event} for user ${this.user.id}`);
|
86
101
|
if (!this.current) {
|
87
102
|
// throw new Error("Telemetry has not been started");
|
88
103
|
return;
|
@@ -129,6 +144,13 @@ const TelemetryManager = {
|
|
129
144
|
}
|
130
145
|
step.ai_interactions.push(data);
|
131
146
|
break;
|
147
|
+
case "quiz_submission": {
|
148
|
+
if (!step.quiz_submissions) {
|
149
|
+
step.quiz_submissions = [];
|
150
|
+
}
|
151
|
+
step.quiz_submissions.push(data);
|
152
|
+
break;
|
153
|
+
}
|
132
154
|
case "open_step": {
|
133
155
|
const now = Date.now();
|
134
156
|
if (!step.opened_at) {
|
@@ -178,13 +200,23 @@ const TelemetryManager = {
|
|
178
200
|
},
|
179
201
|
submit: async function () {
|
180
202
|
console_1.default.debug("Submitting telemetry...");
|
181
|
-
if (!this.current)
|
203
|
+
if (!this.current) {
|
204
|
+
console_1.default.debug("Telemetry has not been started");
|
205
|
+
return Promise.resolve();
|
206
|
+
}
|
207
|
+
if (!this.user.id) {
|
208
|
+
console_1.default.debug("User ID not found, skipping batch telemetry delivery");
|
182
209
|
return Promise.resolve();
|
210
|
+
}
|
183
211
|
const url = this.urls.batch;
|
184
212
|
if (!url) {
|
185
|
-
|
213
|
+
console_1.default.debug("Batch URL not found, skipping batch telemetry delivery");
|
214
|
+
return Promise.resolve();
|
186
215
|
}
|
187
216
|
const body = this.current;
|
217
|
+
if (!body.user_id) {
|
218
|
+
body.user_id = this.user.id;
|
219
|
+
}
|
188
220
|
api_1.default.sendBatchTelemetry(url, body);
|
189
221
|
},
|
190
222
|
save: function () {
|
package/lib/models/session.d.ts
CHANGED
package/lib/utils/api.d.ts
CHANGED
@@ -8,7 +8,7 @@ declare const _default: {
|
|
8
8
|
lang?: string;
|
9
9
|
slug?: string;
|
10
10
|
}) => Promise<any>;
|
11
|
-
sendBatchTelemetry: (url: string, body:
|
11
|
+
sendBatchTelemetry: (url: string, body: any) => Promise<void>;
|
12
12
|
sendStreamTelemetry: (url: string, body: object) => Promise<void>;
|
13
13
|
};
|
14
14
|
export default _default;
|
package/lib/utils/api.js
CHANGED
@@ -207,16 +207,21 @@ const sendBatchTelemetry = async function (url, body) {
|
|
207
207
|
if (!session ||
|
208
208
|
!Object.prototype.hasOwnProperty.call(session, "token") ||
|
209
209
|
session.token === "") {
|
210
|
-
console_1.default.debug("No token found, skipping
|
210
|
+
console_1.default.debug("No token found, skipping batch telemetry delivery");
|
211
|
+
return;
|
212
|
+
}
|
213
|
+
if (!session || !session.user_id || session.user_id === "") {
|
214
|
+
console_1.default.debug("No user_id found, skipping batch telemetry delivery");
|
211
215
|
return;
|
212
216
|
}
|
217
|
+
body.user_id = session.user_id;
|
213
218
|
fetch(url, {
|
214
219
|
method: "POST",
|
215
220
|
body: JSON.stringify(body),
|
216
221
|
}, false)
|
217
222
|
.then(response => {
|
218
223
|
console_1.default.debug("Telemetry sent successfully");
|
219
|
-
return response
|
224
|
+
return response;
|
220
225
|
})
|
221
226
|
.catch(error => {
|
222
227
|
console_1.default.debug("Error while sending batch Telemetry", error);
|
@@ -160,6 +160,7 @@ const checkNotInstalledDependencies = async (neededPlugins) => {
|
|
160
160
|
await installDependencies(jsPluginsDependencies, "npm");
|
161
161
|
}
|
162
162
|
if (pytestNeeded) {
|
163
|
+
await exec("pip install --upgrade pip");
|
163
164
|
const { stdout, stderr } = await exec("pip list");
|
164
165
|
if (stderr) {
|
165
166
|
console_1.default.error(`Error executing pip list. Use debug for more info`);
|
package/oclif.manifest.json
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":"5.0.
|
1
|
+
{"version":"5.0.13","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":[]},"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":{"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":{"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":{},"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":{},"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.13",
|
5
5
|
"author": "Alejandro Sanchez @alesanchezr",
|
6
6
|
"contributors": [
|
7
7
|
{
|
package/src/commands/start.ts
CHANGED
@@ -157,6 +157,7 @@ export default class StartCommand extends SessionCommand {
|
|
157
157
|
compilations: [],
|
158
158
|
tests: [],
|
159
159
|
is_testeable: e.graded || false,
|
160
|
+
quiz_submissions: [],
|
160
161
|
})
|
161
162
|
)
|
162
163
|
if (path && steps.length > 0) {
|
@@ -248,6 +249,11 @@ export default class StartCommand extends SessionCommand {
|
|
248
249
|
})
|
249
250
|
})
|
250
251
|
|
252
|
+
socket.on("quiz_submission", (data: any) => {
|
253
|
+
const { stepPosition, event, eventData } = data
|
254
|
+
TelemetryManager.registerStepEvent(stepPosition, event, eventData)
|
255
|
+
})
|
256
|
+
|
251
257
|
socket.on("ai_interaction", (data: any) => {
|
252
258
|
const { stepPosition, event, eventData } = data
|
253
259
|
TelemetryManager.registerStepEvent(stepPosition, event, eventData)
|
@@ -177,6 +177,12 @@ export default async function (
|
|
177
177
|
|
178
178
|
if (payload && payload.rigobot && payload.rigobot.key) {
|
179
179
|
res.json({ rigoToken: payload.rigobot.key, payload: payload })
|
180
|
+
|
181
|
+
TelemetryManager.setStudent({
|
182
|
+
user_id: payload.user_id,
|
183
|
+
email: payload.email,
|
184
|
+
token: payload.token,
|
185
|
+
})
|
180
186
|
} else {
|
181
187
|
res
|
182
188
|
.status(400)
|
package/src/managers/session.ts
CHANGED
@@ -66,8 +66,15 @@ const Session: ISession = {
|
|
66
66
|
},
|
67
67
|
setPayload: async function (value: IPayload) {
|
68
68
|
await this.initialize()
|
69
|
-
await storage.setItem("bc-payload", {
|
69
|
+
await storage.setItem("bc-payload", { ...value })
|
70
70
|
Console.debug("Payload successfuly found and set for " + value.email)
|
71
|
+
|
72
|
+
// TelemetryManager.setStudent({
|
73
|
+
// user_id: value.user_id.toString(),
|
74
|
+
// email: value.email,
|
75
|
+
// token: value.token,
|
76
|
+
// })
|
77
|
+
|
71
78
|
return true
|
72
79
|
},
|
73
80
|
getPayload: async function () {
|
@@ -128,6 +135,7 @@ const Session: ISession = {
|
|
128
135
|
const data = await api.login(email, password)
|
129
136
|
if (data) {
|
130
137
|
this.start({ token: data.token, payload: data })
|
138
|
+
|
131
139
|
TelemetryManager.setStudent({
|
132
140
|
user_id: data.user_id,
|
133
141
|
email: data.email,
|
@@ -1,353 +1,423 @@
|
|
1
|
-
import { IFile } from "../models/file"
|
2
|
-
import API from "../utils/api"
|
3
|
-
import Console from "../utils/console"
|
4
|
-
|
5
|
-
const fs = require("fs")
|
6
|
-
|
7
|
-
function createUUID(): string {
|
8
|
-
return (
|
9
|
-
Math.random().toString(36).slice(2, 10) +
|
10
|
-
Math.random().toString(36).slice(2, 10)
|
11
|
-
)
|
12
|
-
}
|
13
|
-
|
14
|
-
function stringToBase64(input: string): string {
|
15
|
-
return Buffer.from(input).toString("base64")
|
16
|
-
}
|
17
|
-
|
18
|
-
type TCompilationAttempt = {
|
19
|
-
source_code: string
|
20
|
-
stdout: string
|
21
|
-
exit_code: number
|
22
|
-
starting_at: number
|
23
|
-
ending_at: number
|
24
|
-
}
|
25
|
-
|
26
|
-
type TTestAttempt = {
|
27
|
-
source_code: string
|
28
|
-
stdout: string
|
29
|
-
exit_code: number
|
30
|
-
starting_at: number
|
31
|
-
ending_at: number
|
32
|
-
}
|
33
|
-
|
34
|
-
type TAIInteraction = {
|
35
|
-
student_message: string
|
36
|
-
source_code: string
|
37
|
-
ai_response: string
|
38
|
-
starting_at: number
|
39
|
-
ending_at: number
|
40
|
-
}
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
}
|
53
|
-
|
54
|
-
type
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
(
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
1
|
+
import { IFile } from "../models/file"
|
2
|
+
import API from "../utils/api"
|
3
|
+
import Console from "../utils/console"
|
4
|
+
|
5
|
+
const fs = require("fs")
|
6
|
+
|
7
|
+
function createUUID(): string {
|
8
|
+
return (
|
9
|
+
Math.random().toString(36).slice(2, 10) +
|
10
|
+
Math.random().toString(36).slice(2, 10)
|
11
|
+
)
|
12
|
+
}
|
13
|
+
|
14
|
+
function stringToBase64(input: string): string {
|
15
|
+
return Buffer.from(input).toString("base64")
|
16
|
+
}
|
17
|
+
|
18
|
+
type TCompilationAttempt = {
|
19
|
+
source_code: string
|
20
|
+
stdout: string
|
21
|
+
exit_code: number
|
22
|
+
starting_at: number
|
23
|
+
ending_at: number
|
24
|
+
}
|
25
|
+
|
26
|
+
type TTestAttempt = {
|
27
|
+
source_code: string
|
28
|
+
stdout: string
|
29
|
+
exit_code: number
|
30
|
+
starting_at: number
|
31
|
+
ending_at: number
|
32
|
+
}
|
33
|
+
|
34
|
+
type TAIInteraction = {
|
35
|
+
student_message: string
|
36
|
+
source_code: string
|
37
|
+
ai_response: string
|
38
|
+
starting_at: number
|
39
|
+
ending_at: number
|
40
|
+
}
|
41
|
+
|
42
|
+
type TQuizSelection = {
|
43
|
+
question: string
|
44
|
+
answer: string
|
45
|
+
isCorrect: boolean
|
46
|
+
}
|
47
|
+
|
48
|
+
type TQuizSubmission = {
|
49
|
+
quiz_hash: string
|
50
|
+
selections: TQuizSelection[]
|
51
|
+
submitted_at: number
|
52
|
+
}
|
53
|
+
|
54
|
+
export type TStep = {
|
55
|
+
slug: string
|
56
|
+
position: number
|
57
|
+
files: IFile[]
|
58
|
+
is_testeable: boolean
|
59
|
+
opened_at?: number // The time when the step was opened
|
60
|
+
completed_at?: number // If the step has tests, the time when all the tests passed, else, the time when the user opens the next step
|
61
|
+
compilations: TCompilationAttempt[] // Everytime the user tries to compile the code
|
62
|
+
tests: TTestAttempt[] // Everytime the user tries to run the tests
|
63
|
+
ai_interactions: TAIInteraction[] // Everytime the user interacts with the AI
|
64
|
+
quiz_submissions: TQuizSubmission[] // Everytime the user submits a quiz
|
65
|
+
}
|
66
|
+
|
67
|
+
type TWorkoutSession = {
|
68
|
+
started_at: number
|
69
|
+
ended_at?: number
|
70
|
+
}
|
71
|
+
|
72
|
+
type TStudent = {
|
73
|
+
token: string
|
74
|
+
user_id: string
|
75
|
+
email: string
|
76
|
+
}
|
77
|
+
|
78
|
+
export interface ITelemetryJSONSchema {
|
79
|
+
telemetry_id?: string
|
80
|
+
user_id?: number | string
|
81
|
+
slug: string
|
82
|
+
agent?: string
|
83
|
+
tutorial_started_at: number
|
84
|
+
last_interaction_at: number
|
85
|
+
steps: Array<TStep> // The steps should be the same as the exercise
|
86
|
+
workout_session: TWorkoutSession[] // It start when the user starts Learnpack, if the last_interaction_at is available, it automatically fills with that
|
87
|
+
// number and start another session
|
88
|
+
}
|
89
|
+
|
90
|
+
type TStepEvent =
|
91
|
+
| "compile"
|
92
|
+
| "test"
|
93
|
+
| "ai_interaction"
|
94
|
+
| "open_step"
|
95
|
+
| "quiz_submission"
|
96
|
+
|
97
|
+
export type TTelemetryUrls = {
|
98
|
+
streaming?: string
|
99
|
+
batch?: string
|
100
|
+
}
|
101
|
+
|
102
|
+
type TUser = {
|
103
|
+
token: string
|
104
|
+
id: string
|
105
|
+
email: string
|
106
|
+
}
|
107
|
+
|
108
|
+
interface ITelemetryManager {
|
109
|
+
current: ITelemetryJSONSchema | null
|
110
|
+
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
|
122
|
+
registerStepEvent: (
|
123
|
+
stepPosition: number,
|
124
|
+
event: TStepEvent,
|
125
|
+
data: any
|
126
|
+
) => 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
|
132
|
+
retrieve: () => Promise<ITelemetryJSONSchema | null>
|
133
|
+
}
|
134
|
+
|
135
|
+
const TelemetryManager: ITelemetryManager = {
|
136
|
+
current: null,
|
137
|
+
urls: {},
|
138
|
+
user: {
|
139
|
+
token: "",
|
140
|
+
id: "",
|
141
|
+
email: "",
|
142
|
+
},
|
143
|
+
configPath: "",
|
144
|
+
started: false,
|
145
|
+
salute: message => {
|
146
|
+
Console.info(message)
|
147
|
+
},
|
148
|
+
|
149
|
+
start: function (agent, steps, path, tutorialSlug) {
|
150
|
+
this.configPath = path
|
151
|
+
if (!this.current) {
|
152
|
+
this.retrieve()
|
153
|
+
.then(data => {
|
154
|
+
const prevTelemetry = data
|
155
|
+
if (prevTelemetry) {
|
156
|
+
this.current = prevTelemetry
|
157
|
+
this.finishWorkoutSession()
|
158
|
+
} else {
|
159
|
+
this.current = {
|
160
|
+
telemetry_id: createUUID(),
|
161
|
+
slug: tutorialSlug,
|
162
|
+
agent,
|
163
|
+
tutorial_started_at: Date.now(),
|
164
|
+
last_interaction_at: Date.now(),
|
165
|
+
steps,
|
166
|
+
workout_session: [
|
167
|
+
{
|
168
|
+
started_at: Date.now(),
|
169
|
+
},
|
170
|
+
],
|
171
|
+
}
|
172
|
+
}
|
173
|
+
|
174
|
+
if (this.current.user_id) {
|
175
|
+
this.user.id = this.current.user_id.toString()
|
176
|
+
}
|
177
|
+
|
178
|
+
this.save()
|
179
|
+
|
180
|
+
this.started = true
|
181
|
+
Console.debug("Telemetry started successfully!")
|
182
|
+
|
183
|
+
if (!this.user.id) {
|
184
|
+
Console.debug(
|
185
|
+
"No user ID found, impossible to submit telemetry at start"
|
186
|
+
)
|
187
|
+
return
|
188
|
+
}
|
189
|
+
|
190
|
+
this.submit()
|
191
|
+
})
|
192
|
+
.catch(error => {
|
193
|
+
Console.debug(error)
|
194
|
+
// Delete the telemetry.json if it exists
|
195
|
+
fs.unlinkSync(`${this.configPath}/telemetry.json`)
|
196
|
+
throw new Error(
|
197
|
+
"There was a problem starting, reload LearnPack\nRun\n$ learnpack start"
|
198
|
+
)
|
199
|
+
})
|
200
|
+
}
|
201
|
+
},
|
202
|
+
|
203
|
+
setStudent: function (student) {
|
204
|
+
if (!this.current) {
|
205
|
+
Console.debug("Telemetry has not been started")
|
206
|
+
|
207
|
+
return
|
208
|
+
}
|
209
|
+
|
210
|
+
Console.debug("Setting student", student)
|
211
|
+
|
212
|
+
this.current.user_id = student.user_id
|
213
|
+
this.user.id = student.user_id
|
214
|
+
this.user.token = student.token
|
215
|
+
this.user.email = student.email
|
216
|
+
this.save()
|
217
|
+
this.submit()
|
218
|
+
},
|
219
|
+
finishWorkoutSession: function () {
|
220
|
+
if (!this.current) {
|
221
|
+
return
|
222
|
+
}
|
223
|
+
|
224
|
+
const lastSession =
|
225
|
+
this.current?.workout_session[this.current.workout_session.length - 1]
|
226
|
+
if (
|
227
|
+
lastSession &&
|
228
|
+
!lastSession.ended_at &&
|
229
|
+
this.current?.last_interaction_at
|
230
|
+
) {
|
231
|
+
lastSession.ended_at = this.current.last_interaction_at
|
232
|
+
this.current.workout_session.push({
|
233
|
+
started_at: Date.now(),
|
234
|
+
})
|
235
|
+
}
|
236
|
+
},
|
237
|
+
|
238
|
+
registerStepEvent: function (stepPosition, event, data) {
|
239
|
+
Console.debug(`Registering Event ${event} for user ${this.user.id}`)
|
240
|
+
|
241
|
+
if (!this.current) {
|
242
|
+
// throw new Error("Telemetry has not been started");
|
243
|
+
return
|
244
|
+
}
|
245
|
+
|
246
|
+
const step = this.current.steps[stepPosition]
|
247
|
+
if (!step) {
|
248
|
+
return
|
249
|
+
}
|
250
|
+
|
251
|
+
if (data.source_code) {
|
252
|
+
data.source_code = stringToBase64(data.source_code)
|
253
|
+
}
|
254
|
+
|
255
|
+
if (data.stdout) {
|
256
|
+
data.stdout = stringToBase64(data.stdout)
|
257
|
+
}
|
258
|
+
|
259
|
+
if (data.stderr) {
|
260
|
+
data.stderr = stringToBase64(data.stderr)
|
261
|
+
}
|
262
|
+
|
263
|
+
if (Object.prototype.hasOwnProperty.call(data, "exitCode")) {
|
264
|
+
data.exit_code = data.exitCode
|
265
|
+
data.exitCode = undefined
|
266
|
+
}
|
267
|
+
|
268
|
+
switch (event) {
|
269
|
+
case "compile":
|
270
|
+
if (!step.compilations) {
|
271
|
+
step.compilations = []
|
272
|
+
}
|
273
|
+
|
274
|
+
step.compilations.push(data)
|
275
|
+
this.current.steps[stepPosition] = step
|
276
|
+
break
|
277
|
+
case "test":
|
278
|
+
if (!step.tests) {
|
279
|
+
step.tests = []
|
280
|
+
}
|
281
|
+
|
282
|
+
// data.stdout =
|
283
|
+
step.tests.push(data)
|
284
|
+
if (data.exit_code === 0) {
|
285
|
+
step.completed_at = Date.now()
|
286
|
+
}
|
287
|
+
|
288
|
+
this.current.steps[stepPosition] = step
|
289
|
+
break
|
290
|
+
case "ai_interaction":
|
291
|
+
if (!step.ai_interactions) {
|
292
|
+
step.ai_interactions = []
|
293
|
+
}
|
294
|
+
|
295
|
+
step.ai_interactions.push(data)
|
296
|
+
break
|
297
|
+
|
298
|
+
case "quiz_submission": {
|
299
|
+
if (!step.quiz_submissions) {
|
300
|
+
step.quiz_submissions = []
|
301
|
+
}
|
302
|
+
|
303
|
+
step.quiz_submissions.push(data)
|
304
|
+
break
|
305
|
+
}
|
306
|
+
|
307
|
+
case "open_step": {
|
308
|
+
const now = Date.now()
|
309
|
+
|
310
|
+
if (!step.opened_at) {
|
311
|
+
step.opened_at = now
|
312
|
+
this.current.steps[stepPosition] = step
|
313
|
+
}
|
314
|
+
|
315
|
+
if (this.prevStep || this.prevStep === 0) {
|
316
|
+
const prevStep = this.current.steps[this.prevStep]
|
317
|
+
if (!prevStep.is_testeable && !prevStep.completed_at) {
|
318
|
+
prevStep.completed_at = now
|
319
|
+
this.current.steps[this.prevStep] = prevStep
|
320
|
+
}
|
321
|
+
}
|
322
|
+
|
323
|
+
this.prevStep = stepPosition
|
324
|
+
this.submit()
|
325
|
+
break
|
326
|
+
}
|
327
|
+
|
328
|
+
default:
|
329
|
+
throw new Error(`Event type ${event} is not supported`)
|
330
|
+
}
|
331
|
+
|
332
|
+
this.current.last_interaction_at = Date.now()
|
333
|
+
this.streamEvent(stepPosition, event, data)
|
334
|
+
this.save()
|
335
|
+
},
|
336
|
+
retrieve: function () {
|
337
|
+
return new Promise((resolve, reject) => {
|
338
|
+
fs.readFile(
|
339
|
+
`${this.configPath}/telemetry.json`,
|
340
|
+
"utf8",
|
341
|
+
(err: any, data: any) => {
|
342
|
+
if (err) {
|
343
|
+
if (err.code === "ENOENT") {
|
344
|
+
// File does not exist, resolve with undefined
|
345
|
+
resolve(null)
|
346
|
+
} else {
|
347
|
+
reject(err)
|
348
|
+
}
|
349
|
+
} else {
|
350
|
+
try {
|
351
|
+
resolve(JSON.parse(data))
|
352
|
+
} catch (error) {
|
353
|
+
reject(error)
|
354
|
+
}
|
355
|
+
}
|
356
|
+
}
|
357
|
+
)
|
358
|
+
})
|
359
|
+
},
|
360
|
+
|
361
|
+
submit: async function () {
|
362
|
+
Console.debug("Submitting telemetry...")
|
363
|
+
|
364
|
+
if (!this.current) {
|
365
|
+
Console.debug("Telemetry has not been started")
|
366
|
+
return Promise.resolve()
|
367
|
+
}
|
368
|
+
|
369
|
+
if (!this.user.id) {
|
370
|
+
Console.debug("User ID not found, skipping batch telemetry delivery")
|
371
|
+
return Promise.resolve()
|
372
|
+
}
|
373
|
+
|
374
|
+
const url = this.urls.batch
|
375
|
+
if (!url) {
|
376
|
+
Console.debug("Batch URL not found, skipping batch telemetry delivery")
|
377
|
+
return Promise.resolve()
|
378
|
+
}
|
379
|
+
|
380
|
+
const body = this.current
|
381
|
+
|
382
|
+
if (!body.user_id) {
|
383
|
+
body.user_id = this.user.id
|
384
|
+
}
|
385
|
+
|
386
|
+
API.sendBatchTelemetry(url, body)
|
387
|
+
},
|
388
|
+
save: function () {
|
389
|
+
fs.writeFile(
|
390
|
+
`${this.configPath}/telemetry.json`,
|
391
|
+
JSON.stringify(this.current),
|
392
|
+
(err: any) => {
|
393
|
+
if (err)
|
394
|
+
throw err
|
395
|
+
}
|
396
|
+
)
|
397
|
+
},
|
398
|
+
|
399
|
+
streamEvent: async function (stepPosition, event, data) {
|
400
|
+
if (!this.current)
|
401
|
+
return
|
402
|
+
|
403
|
+
const url = this.urls.streaming
|
404
|
+
if (!url) {
|
405
|
+
return
|
406
|
+
}
|
407
|
+
|
408
|
+
const stepSlug = this.current.steps[stepPosition].slug
|
409
|
+
|
410
|
+
const body = {
|
411
|
+
slug: stepSlug,
|
412
|
+
telemetry_id: this.current.telemetry_id,
|
413
|
+
user_id: this.current.user_id,
|
414
|
+
step_position: stepPosition,
|
415
|
+
event,
|
416
|
+
data,
|
417
|
+
}
|
418
|
+
|
419
|
+
API.sendStreamTelemetry(url, body)
|
420
|
+
},
|
421
|
+
}
|
422
|
+
|
423
|
+
export default TelemetryManager
|
package/src/models/session.ts
CHANGED
package/src/utils/api.ts
CHANGED
@@ -229,7 +229,7 @@ const APIError = (error: TypeError | string, code?: number) => {
|
|
229
229
|
return _err
|
230
230
|
}
|
231
231
|
|
232
|
-
const sendBatchTelemetry = async function (url: string, body:
|
232
|
+
const sendBatchTelemetry = async function (url: string, body: any) {
|
233
233
|
if (!url) {
|
234
234
|
return
|
235
235
|
}
|
@@ -240,10 +240,17 @@ const sendBatchTelemetry = async function (url: string, body: object) {
|
|
240
240
|
!Object.prototype.hasOwnProperty.call(session, "token") ||
|
241
241
|
session.token === ""
|
242
242
|
) {
|
243
|
-
Console.debug("No token found, skipping
|
243
|
+
Console.debug("No token found, skipping batch telemetry delivery")
|
244
|
+
return
|
245
|
+
}
|
246
|
+
|
247
|
+
if (!session || !session.user_id || session.user_id === "") {
|
248
|
+
Console.debug("No user_id found, skipping batch telemetry delivery")
|
244
249
|
return
|
245
250
|
}
|
246
251
|
|
252
|
+
body.user_id = session.user_id
|
253
|
+
|
247
254
|
fetch(
|
248
255
|
url,
|
249
256
|
{
|
@@ -254,7 +261,7 @@ const sendBatchTelemetry = async function (url: string, body: object) {
|
|
254
261
|
)
|
255
262
|
.then(response => {
|
256
263
|
Console.debug("Telemetry sent successfully")
|
257
|
-
return response
|
264
|
+
return response
|
258
265
|
})
|
259
266
|
.catch(error => {
|
260
267
|
Console.debug("Error while sending batch Telemetry", error)
|