@nualang/nualang-ui-components 0.1.1254 → 0.1.1255
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.
|
@@ -8,12 +8,68 @@ var _react = require("react");
|
|
|
8
8
|
var _ResponsiveDialog = _interopRequireDefault(require("../ResponsiveDialog/ResponsiveDialog"));
|
|
9
9
|
var _iconsMaterial = require("@mui/icons-material");
|
|
10
10
|
var _material = require("@mui/material");
|
|
11
|
+
var _styles = require("@mui/material/styles");
|
|
11
12
|
var _reactRouterDom = require("react-router-dom");
|
|
12
13
|
var _Info = _interopRequireDefault(require("@mui/icons-material/Info"));
|
|
13
14
|
var _NualaCreating = _interopRequireDefault(require("../../Misc/NualaCreating/NualaCreating"));
|
|
14
15
|
var _InputHelper = _interopRequireDefault(require("../../Forms/InputHelper/InputHelper"));
|
|
16
|
+
var _mui = require("tss-react/mui");
|
|
15
17
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
16
18
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
19
|
+
const useStyles = (0, _mui.makeStyles)()(theme => ({
|
|
20
|
+
phraseText: {
|
|
21
|
+
width: "80%",
|
|
22
|
+
wordBreak: "break-all"
|
|
23
|
+
},
|
|
24
|
+
phraseTextSmall: {
|
|
25
|
+
width: "60%",
|
|
26
|
+
wordBreak: "break-all"
|
|
27
|
+
},
|
|
28
|
+
avatar: {
|
|
29
|
+
cursor: "pointer",
|
|
30
|
+
margin: theme.spacing()
|
|
31
|
+
},
|
|
32
|
+
avatarNoClick: {
|
|
33
|
+
margin: theme.spacing()
|
|
34
|
+
}
|
|
35
|
+
}));
|
|
36
|
+
function Phrase({
|
|
37
|
+
alternativeVersions,
|
|
38
|
+
translations,
|
|
39
|
+
phrase
|
|
40
|
+
}) {
|
|
41
|
+
const theme = (0, _styles.useTheme)();
|
|
42
|
+
const {
|
|
43
|
+
classes
|
|
44
|
+
} = useStyles();
|
|
45
|
+
const isLargeScreen = (0, _material.useMediaQuery)(theme.breakpoints.up("sm"));
|
|
46
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.ListItem, {
|
|
47
|
+
"data-cy": "phrase-list-item",
|
|
48
|
+
children: isLargeScreen ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Grid, {
|
|
49
|
+
container: true,
|
|
50
|
+
sx: {
|
|
51
|
+
width: "100%"
|
|
52
|
+
},
|
|
53
|
+
className: classes.phraseText,
|
|
54
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.ListItemText, {
|
|
55
|
+
className: classes.phrases,
|
|
56
|
+
primary: `${phrase}${Array.isArray(alternativeVersions) && alternativeVersions.length ? " - " : ""}${(alternativeVersions || []).join(", ")}`,
|
|
57
|
+
secondary: (translations || []).join(", ")
|
|
58
|
+
})
|
|
59
|
+
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Grid, {
|
|
60
|
+
container: true,
|
|
61
|
+
sx: {
|
|
62
|
+
width: "100%"
|
|
63
|
+
},
|
|
64
|
+
className: classes.phraseTextSmall,
|
|
65
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.ListItemText, {
|
|
66
|
+
className: classes.phrases,
|
|
67
|
+
primary: `${phrase}${Array.isArray(alternativeVersions) && alternativeVersions.length ? " - " : ""}${(alternativeVersions || []).join(", ")}`,
|
|
68
|
+
secondary: (translations || []).join(", ")
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
});
|
|
72
|
+
}
|
|
17
73
|
function GeneratePhrases({
|
|
18
74
|
t,
|
|
19
75
|
open,
|
|
@@ -31,6 +87,7 @@ function GeneratePhrases({
|
|
|
31
87
|
const [values, setValues] = (0, _react.useState)(initialValues);
|
|
32
88
|
const [errorMessage, setErrorMessage] = (0, _react.useState)(null);
|
|
33
89
|
const [phraseType, setPhraseType] = (0, _react.useState)("words");
|
|
90
|
+
const [generatedPhrases, setGeneratedPhrases] = (0, _react.useState)([]);
|
|
34
91
|
const isPhrases = currentPhrases && currentPhrases.length > 0;
|
|
35
92
|
const phrases = isPhrases ? currentPhrases.map(({
|
|
36
93
|
phrase,
|
|
@@ -130,22 +187,30 @@ function GeneratePhrases({
|
|
|
130
187
|
const phrasesArray = extractPhrasesArray(chatGptResponse);
|
|
131
188
|
const parsedPhrases = JSON.parse(phrasesArray);
|
|
132
189
|
if (!validateResponse(parsedPhrases)) {
|
|
133
|
-
throw new Error("Invalid response from GPT-
|
|
190
|
+
throw new Error("Invalid response from GPT-4o-mini");
|
|
134
191
|
}
|
|
135
|
-
|
|
192
|
+
setGeneratedPhrases(parsedPhrases);
|
|
136
193
|
setIsGenerating(false);
|
|
137
|
-
handleClose();
|
|
138
194
|
} catch (error) {
|
|
139
195
|
console.error(error);
|
|
140
196
|
setIsGenerating(false);
|
|
141
197
|
setErrorMessage("There was a problem generating phrases. Please try again.");
|
|
142
198
|
}
|
|
143
199
|
};
|
|
200
|
+
const handleConfirm = () => {
|
|
201
|
+
handleAppendPhrases(generatedPhrases);
|
|
202
|
+
setGeneratedPhrases([]);
|
|
203
|
+
handleClose();
|
|
204
|
+
};
|
|
205
|
+
const handleCancel = () => {
|
|
206
|
+
setGeneratedPhrases([]);
|
|
207
|
+
handleClose();
|
|
208
|
+
};
|
|
144
209
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_ResponsiveDialog.default, {
|
|
145
210
|
open: open,
|
|
146
|
-
handleClose:
|
|
147
|
-
handleSubmit: handleGeneratePhrases,
|
|
148
|
-
submitText: t("generate_phrases"),
|
|
211
|
+
handleClose: handleCancel,
|
|
212
|
+
handleSubmit: generatedPhrases.length > 0 ? handleConfirm : handleGeneratePhrases,
|
|
213
|
+
submitText: generatedPhrases.length > 0 ? t("confirm") : t("generate_phrases"),
|
|
149
214
|
dialogTitle: isGenerating ? t("generating_phrases") : t("generate_phrases"),
|
|
150
215
|
isSubmitDisabled: isGenerating,
|
|
151
216
|
isExperimentalFeature: true,
|
|
@@ -170,111 +235,119 @@ function GeneratePhrases({
|
|
|
170
235
|
size: 12,
|
|
171
236
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.LinearProgress, {})
|
|
172
237
|
}), !isGenerating && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
173
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.
|
|
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
|
-
|
|
238
|
+
children: [generatedPhrases.length === 0 && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
239
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Typography, {
|
|
240
|
+
fontSize: 14,
|
|
241
|
+
sx: {
|
|
242
|
+
opacity: 0.8,
|
|
243
|
+
marginBottom: 1
|
|
244
|
+
},
|
|
245
|
+
children: t("required_field")
|
|
246
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Grid, {
|
|
247
|
+
size: 12,
|
|
248
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Alert, {
|
|
249
|
+
severity: "info",
|
|
250
|
+
children: [t("experimental_feature_desc_phrases"), " -", " ", /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactRouterDom.Link, {
|
|
251
|
+
to: "/contact",
|
|
252
|
+
children: [t("contact_form"), "."]
|
|
253
|
+
})]
|
|
254
|
+
})
|
|
255
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Grid, {
|
|
256
|
+
size: 12,
|
|
257
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_InputHelper.default, {
|
|
258
|
+
t: t,
|
|
259
|
+
id: "phrasesTopic",
|
|
260
|
+
name: "phrasesTopic",
|
|
261
|
+
"data-cy": "phrasesTopic",
|
|
262
|
+
label: t("choose_topic_for_phrases"),
|
|
263
|
+
value: values.phrasesTopic,
|
|
264
|
+
type: "text",
|
|
265
|
+
margin: "normal",
|
|
266
|
+
variant: "outlined",
|
|
267
|
+
fullWidth: true,
|
|
268
|
+
required: true,
|
|
269
|
+
onChange: handleChange,
|
|
270
|
+
multiline: true,
|
|
271
|
+
rows: 3
|
|
272
|
+
})
|
|
273
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Grid, {
|
|
274
|
+
mb: 2,
|
|
275
|
+
size: 12,
|
|
276
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TextField, {
|
|
277
|
+
id: "topicGoal",
|
|
278
|
+
name: "topicGoal",
|
|
279
|
+
"data-cy": "topicGoal",
|
|
280
|
+
label: t("topic_goal"),
|
|
281
|
+
placeholder: t("topic_goal_placeholder"),
|
|
282
|
+
value: values.topicGoal,
|
|
283
|
+
type: "text",
|
|
284
|
+
margin: "normal",
|
|
285
|
+
variant: "outlined",
|
|
286
|
+
fullWidth: true,
|
|
287
|
+
onChange: handleChange,
|
|
288
|
+
InputProps: {
|
|
289
|
+
endAdornment: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.InputAdornment, {
|
|
290
|
+
position: "end",
|
|
291
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Tooltip, {
|
|
292
|
+
title: t("topic_goal_info_text"),
|
|
293
|
+
placement: "top",
|
|
294
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Info.default, {
|
|
295
|
+
fontSize: "small",
|
|
296
|
+
style: {
|
|
297
|
+
color: "lightgrey"
|
|
298
|
+
}
|
|
299
|
+
})
|
|
233
300
|
})
|
|
234
301
|
})
|
|
302
|
+
}
|
|
303
|
+
})
|
|
304
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Grid, {
|
|
305
|
+
mb: 2,
|
|
306
|
+
size: 12,
|
|
307
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.TextField, {
|
|
308
|
+
select: true,
|
|
309
|
+
label: t("phrase_type"),
|
|
310
|
+
value: phraseType,
|
|
311
|
+
onChange: e => setPhraseType(e.target.value),
|
|
312
|
+
fullWidth: true,
|
|
313
|
+
variant: "outlined",
|
|
314
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, {
|
|
315
|
+
value: "words",
|
|
316
|
+
children: t("words")
|
|
317
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, {
|
|
318
|
+
value: "sentences",
|
|
319
|
+
children: t("sentences")
|
|
320
|
+
})]
|
|
321
|
+
})
|
|
322
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Grid, {
|
|
323
|
+
size: 12,
|
|
324
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Typography, {
|
|
325
|
+
id: "input-slider",
|
|
326
|
+
gutterBottom: true,
|
|
327
|
+
children: t("how_many_phrases", {
|
|
328
|
+
phraseAmount: values.phraseAmount
|
|
235
329
|
})
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
value: "words",
|
|
250
|
-
children: t("words")
|
|
251
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, {
|
|
252
|
-
value: "sentences",
|
|
253
|
-
children: t("sentences")
|
|
330
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Slider, {
|
|
331
|
+
"aria-label": "Number of phrases",
|
|
332
|
+
defaultValue: values.phraseAmount,
|
|
333
|
+
valueLabelDisplay: "auto",
|
|
334
|
+
value: values.phraseAmount,
|
|
335
|
+
onChange: (e, value) => setValues({
|
|
336
|
+
...values,
|
|
337
|
+
phraseAmount: value
|
|
338
|
+
}),
|
|
339
|
+
step: 2,
|
|
340
|
+
marks: true,
|
|
341
|
+
min: 4,
|
|
342
|
+
max: 16
|
|
254
343
|
})]
|
|
255
|
-
})
|
|
256
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Grid, {
|
|
257
|
-
size: 12,
|
|
258
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Typography, {
|
|
259
|
-
id: "input-slider",
|
|
260
|
-
gutterBottom: true,
|
|
261
|
-
children: t("how_many_phrases", {
|
|
262
|
-
phraseAmount: values.phraseAmount
|
|
263
|
-
})
|
|
264
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Slider, {
|
|
265
|
-
"aria-label": "Number of phrases",
|
|
266
|
-
defaultValue: values.phraseAmount,
|
|
267
|
-
valueLabelDisplay: "auto",
|
|
268
|
-
value: values.phraseAmount,
|
|
269
|
-
onChange: (e, value) => setValues({
|
|
270
|
-
...values,
|
|
271
|
-
phraseAmount: value
|
|
272
|
-
}),
|
|
273
|
-
step: 2,
|
|
274
|
-
marks: true,
|
|
275
|
-
min: 4,
|
|
276
|
-
max: 16
|
|
277
344
|
})]
|
|
345
|
+
}), generatedPhrases.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
346
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Typography, {
|
|
347
|
+
children: t("generated_phrases")
|
|
348
|
+
}), generatedPhrases.map((phrase, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)(Phrase, {
|
|
349
|
+
...phrase
|
|
350
|
+
}, index))]
|
|
278
351
|
}), errorMessage && /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Grid, {
|
|
279
352
|
mt: 2,
|
|
280
353
|
size: 12,
|
|
@@ -346,10 +346,10 @@ function AssignmentCellData({
|
|
|
346
346
|
if (filter === "percentage_complete" && data.percentComplete) {
|
|
347
347
|
cellData = data.percentComplete;
|
|
348
348
|
cellData.type = "percent";
|
|
349
|
-
} else if (filter === "correct_answer_percentage" && data.percentCorrect) {
|
|
349
|
+
} else if (filter === "correct_answer_percentage" && data.percentCorrect !== null) {
|
|
350
350
|
cellData = data.percentCorrect;
|
|
351
351
|
cellData.type = "correct";
|
|
352
|
-
} else if (filter === "avg_pronunciation_score" && data.avgPronunciationScore) {
|
|
352
|
+
} else if (filter === "avg_pronunciation_score" && data.avgPronunciationScore !== null) {
|
|
353
353
|
cellData = data.avgPronunciationScore;
|
|
354
354
|
cellData.type = "avgPronuc";
|
|
355
355
|
}
|
|
@@ -89,25 +89,16 @@ const formatMemberCourseCompletions = (courses = [], completions = [], assignmen
|
|
|
89
89
|
};
|
|
90
90
|
exports.formatMemberCourseCompletions = formatMemberCourseCompletions;
|
|
91
91
|
const formatMemberAssignmentCompletions = (assignments = [], courses = [], completions = []) => {
|
|
92
|
-
const stats = assignments.reduce((obj,
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
if (
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
a.percentCorrect.total += course.percentCorrect.total;
|
|
103
|
-
a.percentCorrect.percentage = Math.floor(a.percentCorrect.completed / a.percentCorrect.total * 100);
|
|
104
|
-
}
|
|
105
|
-
if (a && course && course.avgPronunciationScore && course.avgPronunciationScore.percentage > 0) {
|
|
106
|
-
a.avgPronunciationScore.total += 1;
|
|
107
|
-
a.avgPronunciationScore.percentage = Math.floor(course.avgPronunciationScore.percentage / a.avgPronunciationScore.total);
|
|
108
|
-
}
|
|
109
|
-
return a;
|
|
110
|
-
}, {
|
|
92
|
+
const stats = assignments.reduce((obj, assignment) => {
|
|
93
|
+
const assignmentCompletions = completions.filter(completion => assignment.exercises?.some(e => {
|
|
94
|
+
if (e.name && completion.exercise !== e.name) return false;
|
|
95
|
+
if (e.roleplayId && completion.roleplayId !== e.roleplayId) return false;
|
|
96
|
+
if (e.botId && completion.botId !== e.botId) return false;
|
|
97
|
+
if (e.courseSectionTopicId && `${completion.courseSectionId}|${completion.topicId}` !== e.courseSectionTopicId) return false;
|
|
98
|
+
return true;
|
|
99
|
+
}));
|
|
100
|
+
const assignmentCourseCompletions = formatMemberCourseCompletions(courses, assignmentCompletions, assignment?.exercises);
|
|
101
|
+
const accumulator = {
|
|
111
102
|
percentComplete: {
|
|
112
103
|
completed: 0,
|
|
113
104
|
total: 0,
|
|
@@ -120,10 +111,35 @@ const formatMemberAssignmentCompletions = (assignments = [], courses = [], compl
|
|
|
120
111
|
},
|
|
121
112
|
avgPronunciationScore: {
|
|
122
113
|
total: 0,
|
|
114
|
+
sum: 0,
|
|
123
115
|
percentage: 0
|
|
124
116
|
}
|
|
117
|
+
};
|
|
118
|
+
Object.values(assignmentCourseCompletions).forEach(course => {
|
|
119
|
+
if (course?.percentComplete) {
|
|
120
|
+
accumulator.percentComplete.completed += course.percentComplete.completed || 0;
|
|
121
|
+
accumulator.percentComplete.total += course.percentComplete.total || 0;
|
|
122
|
+
}
|
|
123
|
+
if (course?.percentCorrect) {
|
|
124
|
+
accumulator.percentCorrect.completed += course.percentCorrect.completed || 0;
|
|
125
|
+
accumulator.percentCorrect.total += course.percentCorrect.total || 0;
|
|
126
|
+
}
|
|
127
|
+
if (course?.avgPronunciationScore && course.avgPronunciationScore.percentage >= 0) {
|
|
128
|
+
accumulator.avgPronunciationScore.total += 1;
|
|
129
|
+
accumulator.avgPronunciationScore.sum += course.avgPronunciationScore.percentage;
|
|
130
|
+
}
|
|
125
131
|
});
|
|
126
|
-
|
|
132
|
+
if (accumulator.percentComplete.total > 0) {
|
|
133
|
+
accumulator.percentComplete.percentage = Math.floor(accumulator.percentComplete.completed / accumulator.percentComplete.total * 100);
|
|
134
|
+
}
|
|
135
|
+
if (accumulator.percentCorrect.total > 0) {
|
|
136
|
+
accumulator.percentCorrect.percentage = Math.floor(accumulator.percentCorrect.completed / accumulator.percentCorrect.total * 100);
|
|
137
|
+
}
|
|
138
|
+
if (accumulator.avgPronunciationScore.total > 0) {
|
|
139
|
+
accumulator.avgPronunciationScore.percentage = Math.floor(accumulator.avgPronunciationScore.sum / accumulator.avgPronunciationScore.total);
|
|
140
|
+
}
|
|
141
|
+
delete accumulator.avgPronunciationScore.sum;
|
|
142
|
+
obj[assignment.assignmentId] = accumulator;
|
|
127
143
|
return obj;
|
|
128
144
|
}, {});
|
|
129
145
|
return stats;
|
package/dist/utils/index.js
CHANGED
|
@@ -144,7 +144,15 @@ const getScoreValues = (type, id, completions) => {
|
|
|
144
144
|
percentCorrect.total = totalAnswers;
|
|
145
145
|
percentCorrect.percentage = totalAnswers > 0 ? Math.floor(totalAnswersCorrect / totalAnswers * 100) : 0;
|
|
146
146
|
const avgPronunciationScore = pronunciationScoreCount > 0 ? Math.floor(totalPronunciationScore / pronunciationScoreCount) : -1;
|
|
147
|
-
|
|
147
|
+
if (totalAnswers > 0) {
|
|
148
|
+
return [percentCorrect, avgPronunciationScore];
|
|
149
|
+
} else {
|
|
150
|
+
return [{
|
|
151
|
+
completed: 0,
|
|
152
|
+
total: 0,
|
|
153
|
+
percentage: 0
|
|
154
|
+
}, avgPronunciationScore];
|
|
155
|
+
}
|
|
148
156
|
};
|
|
149
157
|
exports.getScoreValues = getScoreValues;
|
|
150
158
|
const calcTopicCompletions = (topic, completions = [], isSectionHidden, assignmentExercises) => {
|