fcad-core-dragon 2.1.1 → 2.1.2
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/.editorconfig +7 -7
- package/.gitlab-ci.yml +124 -0
- package/.prettierrc +11 -11
- package/.vscode/extensions.json +8 -8
- package/.vscode/settings.json +46 -16
- package/CHANGELOG +520 -520
- package/README.md +57 -57
- package/documentation/.vitepress/config.js +114 -114
- package/documentation/api-examples.md +49 -49
- package/documentation/composants/app-base-button.md +58 -58
- package/documentation/composants/app-base-error-display.md +59 -59
- package/documentation/composants/app-base-popover.md +68 -68
- package/documentation/composants/app-comp-audio.md +75 -75
- package/documentation/composants/app-comp-branch-buttons.md +111 -111
- package/documentation/composants/app-comp-button-progress.md +53 -53
- package/documentation/composants/app-comp-carousel.md +53 -53
- package/documentation/composants/app-comp-container.md +53 -53
- package/documentation/composants/app-comp-input-checkbox-next.md +42 -42
- package/documentation/composants/app-comp-input-dropdown-next.md +34 -34
- package/documentation/composants/app-comp-input-radio-next.md +39 -39
- package/documentation/composants/app-comp-input-text-next.md +35 -35
- package/documentation/composants/app-comp-input-text-table-next.md +34 -34
- package/documentation/composants/app-comp-input-text-to-fill-dropdown-next.md +53 -53
- package/documentation/composants/app-comp-input-text-to-fill-next.md +31 -31
- package/documentation/composants/app-comp-jauge.md +31 -31
- package/documentation/composants/app-comp-menu-item.md +55 -55
- package/documentation/composants/app-comp-menu.md +29 -29
- package/documentation/composants/app-comp-navigation.md +41 -41
- package/documentation/composants/app-comp-note-call.md +53 -53
- package/documentation/composants/app-comp-note-credit.md +53 -53
- package/documentation/composants/app-comp-play-bar-next.md +53 -53
- package/documentation/composants/app-comp-pop-up-next.md +93 -93
- package/documentation/composants/app-comp-quiz-next.md +235 -235
- package/documentation/composants/app-comp-quiz-recall.md +53 -53
- package/documentation/composants/app-comp-svg-next.md +53 -53
- package/documentation/composants/app-comp-table-of-content.md +50 -50
- package/documentation/composants/app-comp-video-player.md +82 -82
- package/documentation/composants.md +46 -46
- package/documentation/composants_critiques/ModelPageComposant.md +53 -53
- package/documentation/composants_critiques/app-base-module.md +43 -43
- package/documentation/composants_critiques/app-base-page.md +48 -48
- package/documentation/composants_critiques/app-base.md +311 -311
- package/documentation/composants_critiques/main.md +15 -15
- package/documentation/demarrage.md +50 -50
- package/documentation/deploiement.md +57 -57
- package/documentation/index.md +33 -33
- package/documentation/markdown-examples.md +85 -85
- package/documentation/public/vite.svg +14 -14
- package/documentation/public/vuejs.svg +1 -1
- package/documentation/public/vuetify.svg +5 -5
- package/eslint.config.js +60 -60
- package/junit-report.xml +182 -0
- package/package.json +66 -59
- package/playwright/index.html +12 -0
- package/playwright/index.js +21 -0
- package/playwright-ct.config.js +95 -0
- package/src/$locales/en.json +157 -157
- package/src/$locales/fr.json +120 -120
- package/src/assets/data/onboardingMessages.json +47 -47
- package/src/components/AppBase.vue +1171 -1169
- package/src/components/AppBaseButton.vue +90 -95
- package/src/components/AppBaseErrorDisplay.vue +438 -438
- package/src/components/AppBaseFlipCard.vue +84 -84
- package/src/components/AppBaseModule.vue +1639 -1634
- package/src/components/AppBasePage.vue +867 -866
- package/src/components/AppBasePopover.vue +41 -41
- package/src/components/AppBaseSkeleton.vue +66 -66
- package/src/components/AppCompAudio.vue +261 -256
- package/src/components/AppCompBranchButtons.vue +508 -508
- package/src/components/AppCompButtonProgress.vue +137 -132
- package/src/components/AppCompCarousel.vue +342 -336
- package/src/components/AppCompContainer.vue +29 -29
- package/src/components/AppCompInputCheckBoxNx.vue +325 -323
- package/src/components/AppCompInputDropdownNx.vue +302 -299
- package/src/components/AppCompInputRadioNx.vue +287 -284
- package/src/components/AppCompInputTextNx.vue +156 -153
- package/src/components/AppCompInputTextTableNx.vue +205 -202
- package/src/components/AppCompInputTextToFillDropdownNx.vue +343 -340
- package/src/components/AppCompInputTextToFillNx.vue +316 -313
- package/src/components/AppCompJauge.vue +81 -81
- package/src/components/AppCompMenu.vue +6 -1
- package/src/components/AppCompMenuItem.vue +246 -240
- package/src/components/AppCompNavigation.vue +977 -972
- package/src/components/AppCompNoteCall.vue +167 -161
- package/src/components/AppCompNoteCredit.vue +496 -491
- package/src/components/AppCompPlayBarNext.vue +2290 -2288
- package/src/components/AppCompPopUpNext.vue +508 -504
- package/src/components/AppCompQuizNext.vue +515 -510
- package/src/components/AppCompQuizRecall.vue +355 -350
- package/src/components/AppCompSVGNext.vue +346 -346
- package/src/components/AppCompSettingsMenu.vue +177 -172
- package/src/components/AppCompTableOfContent.vue +433 -427
- package/src/components/AppCompVideoPlayer.vue +377 -377
- package/src/components/AppCompViewDisplay.vue +6 -6
- package/src/components/BaseModule.vue +55 -55
- package/src/composables/useIdleDetector.js +56 -56
- package/src/composables/useQuiz.js +89 -89
- package/src/composables/useTimer.js +172 -172
- package/src/directives/nvdaFix.js +53 -53
- package/src/externalComps/ModuleView.vue +22 -22
- package/src/externalComps/SummaryView.vue +91 -91
- package/src/main.js +493 -476
- package/src/module/stores/appStore.js +960 -947
- package/src/module/xapi/ADL.js +520 -520
- package/src/module/xapi/Crypto/Hasher.js +241 -241
- package/src/module/xapi/Crypto/WordArray.js +278 -278
- package/src/module/xapi/Crypto/algorithms/BufferedBlockAlgorithm.js +103 -103
- package/src/module/xapi/Crypto/algorithms/C_algo.js +315 -315
- package/src/module/xapi/Crypto/algorithms/HMAC.js +9 -9
- package/src/module/xapi/Crypto/algorithms/SHA1.js +9 -9
- package/src/module/xapi/Crypto/encoders/Base.js +105 -105
- package/src/module/xapi/Crypto/encoders/Base64.js +99 -99
- package/src/module/xapi/Crypto/encoders/Hex.js +61 -61
- package/src/module/xapi/Crypto/encoders/Latin1.js +61 -61
- package/src/module/xapi/Crypto/encoders/Utf8.js +45 -45
- package/src/module/xapi/Crypto/index.js +53 -53
- package/src/module/xapi/Statement/activity.js +47 -47
- package/src/module/xapi/Statement/agent.js +55 -55
- package/src/module/xapi/Statement/group.js +26 -26
- package/src/module/xapi/Statement/index.js +259 -259
- package/src/module/xapi/Statement/statement.js +253 -253
- package/src/module/xapi/Statement/statementRef.js +23 -23
- package/src/module/xapi/Statement/substatement.js +22 -22
- package/src/module/xapi/Statement/verb.js +36 -36
- package/src/module/xapi/activitytypes.js +17 -17
- package/src/module/xapi/launch.js +157 -157
- package/src/module/xapi/utils.js +167 -167
- package/src/module/xapi/verbs.js +294 -294
- package/src/module/xapi/wrapper.js +1895 -1895
- package/src/module/xapi/xapiStatement.js +444 -444
- package/src/plugins/analytics.js +34 -34
- package/src/plugins/bus.js +12 -8
- package/src/plugins/gsap.js +17 -17
- package/src/plugins/helper.js +355 -358
- package/src/plugins/i18n.js +27 -26
- package/src/plugins/idb.js +227 -227
- package/src/plugins/save.js +37 -37
- package/src/plugins/scorm.js +287 -287
- package/src/plugins/xapi.js +11 -11
- package/src/public/index.html +33 -33
- package/src/router/index.js +57 -57
- package/src/router/routes.js +312 -312
- package/src/shared/generalfuncs.js +344 -344
- package/src/shared/validators.js +1018 -1018
- package/tests/component/AppBaseButton.spec.js +53 -0
- package/tests/component/pinia.spec.js +24 -0
- package/{src/components/tests__ → tests/unit}/AppBaseButton.spec.js +53 -53
- package/tests/unit/AppCompInputCheckBoxNx.spec.js +59 -0
- package/tests/unit/AppCompInputDropdownNx.spec.js +51 -0
- package/tests/unit/AppCompInputRadioNx.spec.js +59 -0
- package/tests/unit/AppCompInputTextNx.spec.js +44 -0
- package/tests/unit/AppCompInputTextTableNx.spec.js +77 -0
- package/tests/unit/AppCompInputTextToFillDropdownNx.spec.js +60 -0
- package/tests/unit/AppCompInputTextToFillNx.spec.js +45 -0
- package/tests/unit/AppCompQuizNext.spec.js +114 -0
- package/tests/unit/AppCompVideoPlayer.spec.js +177 -0
- package/{src/components/tests__ → tests/unit}/useTimer.spec.js +91 -91
- package/vitest.config.js +28 -19
- package/vitest.setup.js +28 -0
- package/src/components/AppBaseButton.test.js +0 -21
package/src/module/xapi/ADL.js
CHANGED
|
@@ -1,520 +1,520 @@
|
|
|
1
|
-
import verbs from './verbs'
|
|
2
|
-
import activities from './activitytypes'
|
|
3
|
-
import xAPILaunch from './launch'
|
|
4
|
-
import { defineUtils } from './utils'
|
|
5
|
-
import { xapiwrapper } from './wrapper'
|
|
6
|
-
import { xapiStatement } from './xapiStatement'
|
|
7
|
-
|
|
8
|
-
//ref: https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Communication.md#partthree
|
|
9
|
-
|
|
10
|
-
export default class ADL {
|
|
11
|
-
constructor() {
|
|
12
|
-
xapiwrapper(this)
|
|
13
|
-
defineUtils(this)
|
|
14
|
-
xAPILaunch(this)
|
|
15
|
-
xapiStatement(this)
|
|
16
|
-
|
|
17
|
-
this.verbs = verbs
|
|
18
|
-
this.activityTypes = activities
|
|
19
|
-
|
|
20
|
-
if (this.XAPIWrapper)
|
|
21
|
-
this._prepareStatement = this.XAPIWrapper.prepareStatement
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/*
|
|
25
|
-
* Helper function to configurate the LRS
|
|
26
|
-
* @params: config {Object}
|
|
27
|
-
*/
|
|
28
|
-
_configLRS(config) {
|
|
29
|
-
//this object (XAPIWrapper) will be attached to the ADL
|
|
30
|
-
if (this.XAPIWrapper) {
|
|
31
|
-
this.XAPIWrapper.changeConfig(config)
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/*
|
|
36
|
-
* Helper function to Dispatch statement to the LRS
|
|
37
|
-
* @params: Statement {Object}:xapi Statement
|
|
38
|
-
*/
|
|
39
|
-
_sendStatement(statement, cb, withFetch = false) {
|
|
40
|
-
//remove the statement Result if it not answer action.
|
|
41
|
-
let res = null
|
|
42
|
-
const withResult =
|
|
43
|
-
statement.verb.id.includes('answered') ||
|
|
44
|
-
statement.verb.id.includes('suspended') ||
|
|
45
|
-
statement.verb.id.includes('completed')
|
|
46
|
-
|
|
47
|
-
if (statement.result && !withResult) {
|
|
48
|
-
delete statement.result
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
//Clear existing id of the statement if any to allow xapi to generate new ID for the Statement.
|
|
52
|
-
if (statement.id) statement.id = ''
|
|
53
|
-
|
|
54
|
-
// Dispatch the statement to the LRS
|
|
55
|
-
if (!this.XAPIWrapper) return
|
|
56
|
-
|
|
57
|
-
if (withFetch) this.XAPIWrapper.sendStatementWithFetch(statement)
|
|
58
|
-
else {
|
|
59
|
-
this.XAPIWrapper.sendStatement(statement, function (response) {
|
|
60
|
-
if (cb) cb()
|
|
61
|
-
res = response
|
|
62
|
-
})
|
|
63
|
-
return res
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/*
|
|
68
|
-
* Helper function to Dispatch statement to the LRS
|
|
69
|
-
* @params: Statement {Object}:xapi Statement
|
|
70
|
-
*/
|
|
71
|
-
_sendStatements(statements, cb, withFetch = false) {
|
|
72
|
-
//remove the statement Result if it not answer action.
|
|
73
|
-
let res = null
|
|
74
|
-
statements.forEach((statement) => {
|
|
75
|
-
const withResult =
|
|
76
|
-
statement.verb.id.includes('answered') ||
|
|
77
|
-
statement.verb.id.includes('played') ||
|
|
78
|
-
statement.verb.id.includes('suspended') ||
|
|
79
|
-
statement.verb.id.includes('completed')
|
|
80
|
-
|
|
81
|
-
if (statement.result && !withResult) {
|
|
82
|
-
delete statement.result
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
//Clear existing id of the statement if any to allow xapi to generate new ID for the Statement.
|
|
86
|
-
if (statement.id) statement.id = ''
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
// Dispatch the statement to the LRS
|
|
90
|
-
if (!this.XAPIWrapper) return
|
|
91
|
-
|
|
92
|
-
if (withFetch) this.XAPIWrapper.sendStatementsWithFetch(statements)
|
|
93
|
-
else {
|
|
94
|
-
this.XAPIWrapper.sendStatements(statements, function (response) {
|
|
95
|
-
if (cb) cb()
|
|
96
|
-
res = response
|
|
97
|
-
})
|
|
98
|
-
return res
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/*
|
|
103
|
-
* Helper function to get the agent email
|
|
104
|
-
* @Params: {String} email : email of the user
|
|
105
|
-
* @Params: {String} activityId: IRI string of the course/activity id
|
|
106
|
-
* @Params: {String} verb_id: IRI string of the course/activity id
|
|
107
|
-
*
|
|
108
|
-
*/
|
|
109
|
-
_getAgent(email, activityId, verb_id) {
|
|
110
|
-
let params, // Array of search parameters
|
|
111
|
-
completedStmts // Array of return search result
|
|
112
|
-
|
|
113
|
-
if (this.XAPIWrapper) {
|
|
114
|
-
params = this.XAPIWrapper.searchParams()
|
|
115
|
-
|
|
116
|
-
if (email) params['agent'] = `{"mbox": "mailto:${email}"}`
|
|
117
|
-
if (activityId) params['activity'] = activityId
|
|
118
|
-
if (verb_id) params['verb'] = verb_id
|
|
119
|
-
|
|
120
|
-
completedStmts = this.XAPIWrapper.getStatements(params)
|
|
121
|
-
}
|
|
122
|
-
//There is a records for the search return the actor
|
|
123
|
-
if (
|
|
124
|
-
completedStmts &&
|
|
125
|
-
completedStmts.statements &&
|
|
126
|
-
completedStmts.statements.length
|
|
127
|
-
)
|
|
128
|
-
return completedStmts.statements[0].actor
|
|
129
|
-
else return false // no result
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/*@Description: Helper to retrive user progress -
|
|
133
|
-
search for a completion status of a activity if there is no completion status
|
|
134
|
-
* search where the user stopped in the activity
|
|
135
|
-
* return is position in the activity
|
|
136
|
-
*
|
|
137
|
-
* @Params {String} email: user email
|
|
138
|
-
* @Params {String} activityId: the IRI string of the activity
|
|
139
|
-
* @Params {String} verb: the verb to search by completed | terminated
|
|
140
|
-
*/
|
|
141
|
-
|
|
142
|
-
async _getProgress(email, activityId, verb) {
|
|
143
|
-
let params
|
|
144
|
-
const validVerbs = [
|
|
145
|
-
'progressed',
|
|
146
|
-
'completed',
|
|
147
|
-
'suspended',
|
|
148
|
-
'terminated',
|
|
149
|
-
'exited'
|
|
150
|
-
]
|
|
151
|
-
if (this.XAPIWrapper) params = this.XAPIWrapper.searchParams()
|
|
152
|
-
|
|
153
|
-
let vb = verb && validVerbs.includes(verb) ? verb : 'terminated'
|
|
154
|
-
|
|
155
|
-
params['verb'] = this.verbs[vb].id // completed state
|
|
156
|
-
params['activity'] = activityId // this current activity define by this id
|
|
157
|
-
params['agent'] = `{"mbox": "mailto:${email}"}` // the user identify by this email
|
|
158
|
-
params['limit'] = 200 //limit of record to return by the LRS
|
|
159
|
-
params['ascending'] = true //ordered by the oldest statement recorded
|
|
160
|
-
|
|
161
|
-
let date = new Date()
|
|
162
|
-
let dd = new Date(date.setDate(date.getDate() + 1)).toISOString() //Date is set to next day(tomorrow) for Lrs to take in account statement registered today.
|
|
163
|
-
params['until'] = dd
|
|
164
|
-
|
|
165
|
-
try {
|
|
166
|
-
let userData //placeholder for user data
|
|
167
|
-
let completedStmts = await this.XAPIWrapper.getStatements(params)
|
|
168
|
-
|
|
169
|
-
if (
|
|
170
|
-
completedStmts &&
|
|
171
|
-
completedStmts.statements &&
|
|
172
|
-
completedStmts.statements.length
|
|
173
|
-
) {
|
|
174
|
-
let lastRecord = completedStmts.statements.toReversed()[0] // get last recorded statement
|
|
175
|
-
|
|
176
|
-
// Verify that the extensions value of the statement as elements
|
|
177
|
-
if (
|
|
178
|
-
lastRecord.object.definition.extensions &&
|
|
179
|
-
Object.keys(lastRecord.object.definition.extensions).length
|
|
180
|
-
) {
|
|
181
|
-
const allExtensionsKeys = Object.keys(
|
|
182
|
-
lastRecord.object.definition.extensions
|
|
183
|
-
)
|
|
184
|
-
|
|
185
|
-
//Search for the user-data entry in the extensions
|
|
186
|
-
const userDataExtension = allExtensionsKeys.filter((extension) =>
|
|
187
|
-
extension.includes('user-data')
|
|
188
|
-
)
|
|
189
|
-
userData =
|
|
190
|
-
lastRecord.object.definition.extensions[userDataExtension[0]]
|
|
191
|
-
}
|
|
192
|
-
} else {
|
|
193
|
-
userData = {}
|
|
194
|
-
}
|
|
195
|
-
return userData
|
|
196
|
-
} catch (err) {
|
|
197
|
-
console.log(err)
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/*@Description: Helper to The Lesson Status (result) -
|
|
202
|
-
|
|
203
|
-
* @Params {String} email: user email
|
|
204
|
-
* @Params {String} activityId: the IRI string of the activity
|
|
205
|
-
*/
|
|
206
|
-
async _getLessonStatus(email, activityId, verb) {
|
|
207
|
-
let params
|
|
208
|
-
const validVerbs = ['completed', 'suspended']
|
|
209
|
-
if (this.XAPIWrapper) params = this.XAPIWrapper.searchParams()
|
|
210
|
-
let vb = verb && validVerbs.includes(verb) ? verb : 'suspended'
|
|
211
|
-
params['verb'] = this.verbs[vb].id // completed state
|
|
212
|
-
params['activity'] = activityId // this current activity define by this id
|
|
213
|
-
params['agent'] = `{"mbox": "mailto:${email}"}` // the user identify by this email
|
|
214
|
-
params['ascending'] = true //ordered by the oldest statement recorded
|
|
215
|
-
|
|
216
|
-
let completedStmts = await this.XAPIWrapper.getStatements(params)
|
|
217
|
-
let status //placeholder for user data
|
|
218
|
-
if (
|
|
219
|
-
completedStmts &&
|
|
220
|
-
completedStmts.statements &&
|
|
221
|
-
completedStmts.statements.length
|
|
222
|
-
) {
|
|
223
|
-
let lastRecord = completedStmts.statements.toReversed()[0] // get last recorded statement
|
|
224
|
-
|
|
225
|
-
// Verify that result exist
|
|
226
|
-
if (lastRecord.result) status = lastRecord.result
|
|
227
|
-
} else {
|
|
228
|
-
status = {}
|
|
229
|
-
}
|
|
230
|
-
return status
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/* @description: last lesson saved point from the LRS
|
|
234
|
-
* search where the user stopped in the activity
|
|
235
|
-
* return is position in the activity
|
|
236
|
-
* @Params {String} email: user email
|
|
237
|
-
* @Params {String} activityId: the IRI string of the activity
|
|
238
|
-
*/
|
|
239
|
-
|
|
240
|
-
async _getLessonPosition(email, activityId) {
|
|
241
|
-
let params
|
|
242
|
-
let savedPoint = []
|
|
243
|
-
if (this.XAPIWrapper) params = this.XAPIWrapper.searchParams()
|
|
244
|
-
|
|
245
|
-
params['verb'] = this.verbs.terminated.id // completed state
|
|
246
|
-
params['activity'] = activityId // this current activity define by this id
|
|
247
|
-
params['agent'] = `{"mbox": "mailto:${email}"}` // the user identify by this email
|
|
248
|
-
params['ascending'] = true //ordered by the oldest statement recorded
|
|
249
|
-
params['limit'] = 200 //limit of record to return by the LRS
|
|
250
|
-
|
|
251
|
-
let date = new Date()
|
|
252
|
-
let dd = new Date(date.setDate(date.getDate() + 1)).toISOString() //Date is set to next day(tomorrow) for Lrs to take in account statement registered today.
|
|
253
|
-
|
|
254
|
-
params['until'] = dd
|
|
255
|
-
|
|
256
|
-
let progressedStmts = await this.XAPIWrapper.getStatements(params)
|
|
257
|
-
|
|
258
|
-
if (
|
|
259
|
-
progressedStmts &&
|
|
260
|
-
progressedStmts.statements &&
|
|
261
|
-
progressedStmts.statements.length
|
|
262
|
-
) {
|
|
263
|
-
let lastRecord = progressedStmts.statements.toReversed()[0] // get last recorded statement
|
|
264
|
-
// Verify that the extensions value of the statement has elements
|
|
265
|
-
|
|
266
|
-
if (
|
|
267
|
-
lastRecord.object.definition.extensions &&
|
|
268
|
-
Object.keys(lastRecord.object.definition.extensions).length
|
|
269
|
-
) {
|
|
270
|
-
const allExtensionsKeys = Object.keys(
|
|
271
|
-
lastRecord.object.definition.extensions
|
|
272
|
-
)
|
|
273
|
-
|
|
274
|
-
//Search for the user-data entry in the extensions
|
|
275
|
-
savedPoint = allExtensionsKeys.map((extension) => {
|
|
276
|
-
if (extension.includes('ending-point'))
|
|
277
|
-
return lastRecord.object.definition.extensions[extension]
|
|
278
|
-
})
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
return savedPoint && savedPoint.length > 0 ? savedPoint[0] : ''
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/* @description: Helper to get the Preferred Settings of the user
|
|
286
|
-
* return is position in the activity
|
|
287
|
-
* @Params {String} email: user email
|
|
288
|
-
* @Params {String} activityId: the IRI string of the activity
|
|
289
|
-
*/
|
|
290
|
-
|
|
291
|
-
async _getPreferredSettings(email, activityId) {
|
|
292
|
-
let params
|
|
293
|
-
let preferredSettings = []
|
|
294
|
-
if (this.XAPIWrapper) params = this.XAPIWrapper.searchParams()
|
|
295
|
-
|
|
296
|
-
params['verb'] = this.verbs.preferred.id // completed state
|
|
297
|
-
params['activity'] = activityId // this current activity define by this id
|
|
298
|
-
params['agent'] = `{"mbox": "mailto:${email}"}` // the user identify by this email
|
|
299
|
-
params['ascending'] = true //ordered by the oldest statement recorded
|
|
300
|
-
params['limit'] = 200 //limit of record to return by the LRS
|
|
301
|
-
|
|
302
|
-
let date = new Date()
|
|
303
|
-
let dd = new Date(date.setDate(date.getDate() + 1)).toISOString() //Date is set to next day(tomorrow) for Lrs to take in account statement registered today.
|
|
304
|
-
|
|
305
|
-
params['until'] = dd
|
|
306
|
-
|
|
307
|
-
let preferredStmts = await this.XAPIWrapper.getStatements(params)
|
|
308
|
-
|
|
309
|
-
if (
|
|
310
|
-
preferredStmts &&
|
|
311
|
-
preferredStmts.statements &&
|
|
312
|
-
preferredStmts.statements.length
|
|
313
|
-
) {
|
|
314
|
-
let lastRecord = preferredStmts.statements.toReversed()[0] // get last recorded statement
|
|
315
|
-
|
|
316
|
-
// Verify that the extensions value of the statement has elements
|
|
317
|
-
if (
|
|
318
|
-
lastRecord.object.definition.extensions &&
|
|
319
|
-
Object.keys(lastRecord.object.definition.extensions).length
|
|
320
|
-
) {
|
|
321
|
-
const allExtensionsKeys = Object.keys(
|
|
322
|
-
lastRecord.object.definition.extensions
|
|
323
|
-
)
|
|
324
|
-
|
|
325
|
-
//Search for the user-data entry in the extensions
|
|
326
|
-
preferredSettings = allExtensionsKeys.map((extension) => {
|
|
327
|
-
if (extension.includes('application-settings'))
|
|
328
|
-
return lastRecord.object.definition.extensions[extension]
|
|
329
|
-
})
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
return preferredSettings[0] ? preferredSettings[0] : {}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
/* @description: Helper to get the Playbar values of the user
|
|
336
|
-
* @Params {String} email: user email
|
|
337
|
-
* @Params {String} activityId: the IRI string of the activity
|
|
338
|
-
*/
|
|
339
|
-
|
|
340
|
-
async _getPlaybarValues(email, activityId) {
|
|
341
|
-
let params
|
|
342
|
-
let playbarValues = []
|
|
343
|
-
if (this.XAPIWrapper) params = this.XAPIWrapper.searchParams()
|
|
344
|
-
|
|
345
|
-
params['verb'] = this.verbs.played.id // completed state
|
|
346
|
-
params['activity'] = activityId // this current activity define by this id
|
|
347
|
-
params['agent'] = `{"mbox": "mailto:${email}"}` // the user identify by this email
|
|
348
|
-
params['ascending'] = true //ordered by the oldest statement recorded
|
|
349
|
-
params['limit'] = 200 //limit of record to return by the LRS
|
|
350
|
-
|
|
351
|
-
let date = new Date()
|
|
352
|
-
let dd = new Date(date.setDate(date.getDate() + 1)).toISOString() //Date is set to next day(tomorrow) for Lrs to take in account statement registered today.
|
|
353
|
-
|
|
354
|
-
params['until'] = dd
|
|
355
|
-
|
|
356
|
-
let playedStmts = await this.XAPIWrapper.getStatements(params)
|
|
357
|
-
|
|
358
|
-
if (
|
|
359
|
-
playedStmts &&
|
|
360
|
-
playedStmts.statements &&
|
|
361
|
-
playedStmts.statements.length
|
|
362
|
-
) {
|
|
363
|
-
let lastRecord = playedStmts.statements.toReversed()[0] // get last recorded statement
|
|
364
|
-
// Verify that the extensions value of the statement has elements
|
|
365
|
-
if (
|
|
366
|
-
lastRecord.object.definition.extensions &&
|
|
367
|
-
Object.keys(lastRecord.object.definition.extensions).length
|
|
368
|
-
) {
|
|
369
|
-
const allExtensionsKeys = Object.keys(
|
|
370
|
-
lastRecord.object.definition.extensions
|
|
371
|
-
)
|
|
372
|
-
//Search for the user-data entry in the extensions
|
|
373
|
-
playbarValues = allExtensionsKeys.map((extension) => {
|
|
374
|
-
if (extension.includes('playbar-values'))
|
|
375
|
-
return lastRecord.object.definition.extensions[extension]
|
|
376
|
-
})
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
return playbarValues[0] ? playbarValues[0] : {}
|
|
380
|
-
}
|
|
381
|
-
/**
|
|
382
|
-
* Method to get a multiple concurent requests using promiseAll to the server
|
|
383
|
-
* and return all data at once.
|
|
384
|
-
* the following request are made to the server:
|
|
385
|
-
* get application-settings: saved value for the user preferred settings
|
|
386
|
-
* get user-data: saved values for user progression
|
|
387
|
-
* get playbar-value : saved value for the medias play-bar
|
|
388
|
-
* get lesson-status : saved value for the lession status
|
|
389
|
-
*
|
|
390
|
-
* @param {Array} searchParams - Array of Object with key value representing each search paremeter.
|
|
391
|
-
* a parameter object definition is expected to have the following signature:
|
|
392
|
-
* {
|
|
393
|
-
* email{String} agent email,
|
|
394
|
-
* verb {String} verb for the request,
|
|
395
|
-
* activityId {String} - activity id ,
|
|
396
|
-
* }
|
|
397
|
-
* @example here is an exemple of the search param:
|
|
398
|
-
* {email: 'wizardy0@mymailbox.com', verb: 'completed',activityId:'"http://localhost:5173/vuetify_p4/v3"' }
|
|
399
|
-
* @return {object} data with a collection of all the reponses
|
|
400
|
-
* from the promiseAll and the status of last request
|
|
401
|
-
*/
|
|
402
|
-
async _getBulkData(searchParams) {
|
|
403
|
-
const validVerbs = [
|
|
404
|
-
'progressed',
|
|
405
|
-
'completed',
|
|
406
|
-
'suspended',
|
|
407
|
-
'terminated',
|
|
408
|
-
'preferred',
|
|
409
|
-
'played',
|
|
410
|
-
'exited'
|
|
411
|
-
]
|
|
412
|
-
|
|
413
|
-
const paramList = []
|
|
414
|
-
// account statement registered today.
|
|
415
|
-
let date = new Date()
|
|
416
|
-
let dd = new Date(date.setDate(date.getDate() + 1)).toISOString() //Date is set to next day(tomorrow) for Lrs to take in
|
|
417
|
-
|
|
418
|
-
for (let param of searchParams) {
|
|
419
|
-
let { email, verb, activityId } = param
|
|
420
|
-
let vb = verb && validVerbs.includes(verb) ? verb : 'terminated'
|
|
421
|
-
const params = {}
|
|
422
|
-
|
|
423
|
-
params['verb'] = this.verbs[vb].id // completed state
|
|
424
|
-
params['activity'] = activityId // this current activity define by this id
|
|
425
|
-
params['agent'] = `{"mbox": "mailto:${email}"}` // the user identify by this email
|
|
426
|
-
params['ascending'] = true //ordered by the oldest statement recorded
|
|
427
|
-
params['limit'] = 250 //limit of record to return by the LRS
|
|
428
|
-
|
|
429
|
-
params['until'] = dd
|
|
430
|
-
paramList.push(params)
|
|
431
|
-
}
|
|
432
|
-
const { response } = await this.XAPIWrapper.getStatements(paramList)
|
|
433
|
-
|
|
434
|
-
let userData = {},
|
|
435
|
-
playbarValues = {},
|
|
436
|
-
preferredSettings = {},
|
|
437
|
-
lessonStatus = {},
|
|
438
|
-
savedPoint = ''
|
|
439
|
-
|
|
440
|
-
if (response && response.length > 0) {
|
|
441
|
-
for (const res of response) {
|
|
442
|
-
const { statements } = await res.json()
|
|
443
|
-
if (!statements || statements.length == 0) continue
|
|
444
|
-
|
|
445
|
-
let lastRecord = statements.toReversed()[0] // get last recorded statement
|
|
446
|
-
// Verify that the extensions value of the statement has elements
|
|
447
|
-
|
|
448
|
-
//Handeling Lession comppletion status
|
|
449
|
-
if (lastRecord.verb.id.includes('completed')) {
|
|
450
|
-
lessonStatus = lastRecord.result ? lastRecord.result : {}
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
if (
|
|
454
|
-
lastRecord.object.definition.extensions &&
|
|
455
|
-
Object.keys(lastRecord.object.definition.extensions).length
|
|
456
|
-
) {
|
|
457
|
-
const allExtensionsKeys = Object.keys(
|
|
458
|
-
lastRecord.object.definition.extensions
|
|
459
|
-
)
|
|
460
|
-
|
|
461
|
-
//Search for the user-data entry in the extensions
|
|
462
|
-
allExtensionsKeys.forEach((extension) => {
|
|
463
|
-
switch (true) {
|
|
464
|
-
case extension.includes('user-data'): {
|
|
465
|
-
//Search for the user-data entry in the extensions
|
|
466
|
-
const userDataExtension = allExtensionsKeys.filter(
|
|
467
|
-
(extension) => extension.includes('user-data')
|
|
468
|
-
)
|
|
469
|
-
userData =
|
|
470
|
-
lastRecord.object.definition.extensions[userDataExtension[0]]
|
|
471
|
-
|
|
472
|
-
break
|
|
473
|
-
}
|
|
474
|
-
case extension.includes('ending-point'): {
|
|
475
|
-
const userDataExtension = allExtensionsKeys.filter(
|
|
476
|
-
(extension) => extension.includes('ending-point')
|
|
477
|
-
)
|
|
478
|
-
savedPoint =
|
|
479
|
-
lastRecord.object.definition.extensions[userDataExtension[0]]
|
|
480
|
-
|
|
481
|
-
break
|
|
482
|
-
}
|
|
483
|
-
case extension.includes('application-settings'): {
|
|
484
|
-
const userDataExtension = allExtensionsKeys.filter(
|
|
485
|
-
(extension) => extension.includes('application-settings')
|
|
486
|
-
)
|
|
487
|
-
|
|
488
|
-
const settingsData =
|
|
489
|
-
lastRecord.object.definition.extensions[userDataExtension[0]]
|
|
490
|
-
|
|
491
|
-
preferredSettings = settingsData ? settingsData : {}
|
|
492
|
-
|
|
493
|
-
break
|
|
494
|
-
}
|
|
495
|
-
case extension.includes('playbar-values'): {
|
|
496
|
-
const userDataExtension = allExtensionsKeys.filter(
|
|
497
|
-
(extension) => extension.includes('playbar-values')
|
|
498
|
-
)
|
|
499
|
-
|
|
500
|
-
const playbarData =
|
|
501
|
-
lastRecord.object.definition.extensions[userDataExtension[0]]
|
|
502
|
-
playbarValues = playbarData ? playbarData : {}
|
|
503
|
-
|
|
504
|
-
break
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
})
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
return {
|
|
513
|
-
userData,
|
|
514
|
-
savedPoint,
|
|
515
|
-
preferredSettings,
|
|
516
|
-
playbarValues,
|
|
517
|
-
lessonStatus
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
}
|
|
1
|
+
import verbs from './verbs'
|
|
2
|
+
import activities from './activitytypes'
|
|
3
|
+
import xAPILaunch from './launch'
|
|
4
|
+
import { defineUtils } from './utils'
|
|
5
|
+
import { xapiwrapper } from './wrapper'
|
|
6
|
+
import { xapiStatement } from './xapiStatement'
|
|
7
|
+
|
|
8
|
+
//ref: https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Communication.md#partthree
|
|
9
|
+
|
|
10
|
+
export default class ADL {
|
|
11
|
+
constructor() {
|
|
12
|
+
xapiwrapper(this)
|
|
13
|
+
defineUtils(this)
|
|
14
|
+
xAPILaunch(this)
|
|
15
|
+
xapiStatement(this)
|
|
16
|
+
|
|
17
|
+
this.verbs = verbs
|
|
18
|
+
this.activityTypes = activities
|
|
19
|
+
|
|
20
|
+
if (this.XAPIWrapper)
|
|
21
|
+
this._prepareStatement = this.XAPIWrapper.prepareStatement
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/*
|
|
25
|
+
* Helper function to configurate the LRS
|
|
26
|
+
* @params: config {Object}
|
|
27
|
+
*/
|
|
28
|
+
_configLRS(config) {
|
|
29
|
+
//this object (XAPIWrapper) will be attached to the ADL
|
|
30
|
+
if (this.XAPIWrapper) {
|
|
31
|
+
this.XAPIWrapper.changeConfig(config)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/*
|
|
36
|
+
* Helper function to Dispatch statement to the LRS
|
|
37
|
+
* @params: Statement {Object}:xapi Statement
|
|
38
|
+
*/
|
|
39
|
+
_sendStatement(statement, cb, withFetch = false) {
|
|
40
|
+
//remove the statement Result if it not answer action.
|
|
41
|
+
let res = null
|
|
42
|
+
const withResult =
|
|
43
|
+
statement.verb.id.includes('answered') ||
|
|
44
|
+
statement.verb.id.includes('suspended') ||
|
|
45
|
+
statement.verb.id.includes('completed')
|
|
46
|
+
|
|
47
|
+
if (statement.result && !withResult) {
|
|
48
|
+
delete statement.result
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
//Clear existing id of the statement if any to allow xapi to generate new ID for the Statement.
|
|
52
|
+
if (statement.id) statement.id = ''
|
|
53
|
+
|
|
54
|
+
// Dispatch the statement to the LRS
|
|
55
|
+
if (!this.XAPIWrapper) return
|
|
56
|
+
|
|
57
|
+
if (withFetch) this.XAPIWrapper.sendStatementWithFetch(statement)
|
|
58
|
+
else {
|
|
59
|
+
this.XAPIWrapper.sendStatement(statement, function (response) {
|
|
60
|
+
if (cb) cb()
|
|
61
|
+
res = response
|
|
62
|
+
})
|
|
63
|
+
return res
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/*
|
|
68
|
+
* Helper function to Dispatch statement to the LRS
|
|
69
|
+
* @params: Statement {Object}:xapi Statement
|
|
70
|
+
*/
|
|
71
|
+
_sendStatements(statements, cb, withFetch = false) {
|
|
72
|
+
//remove the statement Result if it not answer action.
|
|
73
|
+
let res = null
|
|
74
|
+
statements.forEach((statement) => {
|
|
75
|
+
const withResult =
|
|
76
|
+
statement.verb.id.includes('answered') ||
|
|
77
|
+
statement.verb.id.includes('played') ||
|
|
78
|
+
statement.verb.id.includes('suspended') ||
|
|
79
|
+
statement.verb.id.includes('completed')
|
|
80
|
+
|
|
81
|
+
if (statement.result && !withResult) {
|
|
82
|
+
delete statement.result
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
//Clear existing id of the statement if any to allow xapi to generate new ID for the Statement.
|
|
86
|
+
if (statement.id) statement.id = ''
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
// Dispatch the statement to the LRS
|
|
90
|
+
if (!this.XAPIWrapper) return
|
|
91
|
+
|
|
92
|
+
if (withFetch) this.XAPIWrapper.sendStatementsWithFetch(statements)
|
|
93
|
+
else {
|
|
94
|
+
this.XAPIWrapper.sendStatements(statements, function (response) {
|
|
95
|
+
if (cb) cb()
|
|
96
|
+
res = response
|
|
97
|
+
})
|
|
98
|
+
return res
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/*
|
|
103
|
+
* Helper function to get the agent email
|
|
104
|
+
* @Params: {String} email : email of the user
|
|
105
|
+
* @Params: {String} activityId: IRI string of the course/activity id
|
|
106
|
+
* @Params: {String} verb_id: IRI string of the course/activity id
|
|
107
|
+
*
|
|
108
|
+
*/
|
|
109
|
+
_getAgent(email, activityId, verb_id) {
|
|
110
|
+
let params, // Array of search parameters
|
|
111
|
+
completedStmts // Array of return search result
|
|
112
|
+
|
|
113
|
+
if (this.XAPIWrapper) {
|
|
114
|
+
params = this.XAPIWrapper.searchParams()
|
|
115
|
+
|
|
116
|
+
if (email) params['agent'] = `{"mbox": "mailto:${email}"}`
|
|
117
|
+
if (activityId) params['activity'] = activityId
|
|
118
|
+
if (verb_id) params['verb'] = verb_id
|
|
119
|
+
|
|
120
|
+
completedStmts = this.XAPIWrapper.getStatements(params)
|
|
121
|
+
}
|
|
122
|
+
//There is a records for the search return the actor
|
|
123
|
+
if (
|
|
124
|
+
completedStmts &&
|
|
125
|
+
completedStmts.statements &&
|
|
126
|
+
completedStmts.statements.length
|
|
127
|
+
)
|
|
128
|
+
return completedStmts.statements[0].actor
|
|
129
|
+
else return false // no result
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/*@Description: Helper to retrive user progress -
|
|
133
|
+
search for a completion status of a activity if there is no completion status
|
|
134
|
+
* search where the user stopped in the activity
|
|
135
|
+
* return is position in the activity
|
|
136
|
+
*
|
|
137
|
+
* @Params {String} email: user email
|
|
138
|
+
* @Params {String} activityId: the IRI string of the activity
|
|
139
|
+
* @Params {String} verb: the verb to search by completed | terminated
|
|
140
|
+
*/
|
|
141
|
+
|
|
142
|
+
async _getProgress(email, activityId, verb) {
|
|
143
|
+
let params
|
|
144
|
+
const validVerbs = [
|
|
145
|
+
'progressed',
|
|
146
|
+
'completed',
|
|
147
|
+
'suspended',
|
|
148
|
+
'terminated',
|
|
149
|
+
'exited'
|
|
150
|
+
]
|
|
151
|
+
if (this.XAPIWrapper) params = this.XAPIWrapper.searchParams()
|
|
152
|
+
|
|
153
|
+
let vb = verb && validVerbs.includes(verb) ? verb : 'terminated'
|
|
154
|
+
|
|
155
|
+
params['verb'] = this.verbs[vb].id // completed state
|
|
156
|
+
params['activity'] = activityId // this current activity define by this id
|
|
157
|
+
params['agent'] = `{"mbox": "mailto:${email}"}` // the user identify by this email
|
|
158
|
+
params['limit'] = 200 //limit of record to return by the LRS
|
|
159
|
+
params['ascending'] = true //ordered by the oldest statement recorded
|
|
160
|
+
|
|
161
|
+
let date = new Date()
|
|
162
|
+
let dd = new Date(date.setDate(date.getDate() + 1)).toISOString() //Date is set to next day(tomorrow) for Lrs to take in account statement registered today.
|
|
163
|
+
params['until'] = dd
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
let userData //placeholder for user data
|
|
167
|
+
let completedStmts = await this.XAPIWrapper.getStatements(params)
|
|
168
|
+
|
|
169
|
+
if (
|
|
170
|
+
completedStmts &&
|
|
171
|
+
completedStmts.statements &&
|
|
172
|
+
completedStmts.statements.length
|
|
173
|
+
) {
|
|
174
|
+
let lastRecord = completedStmts.statements.toReversed()[0] // get last recorded statement
|
|
175
|
+
|
|
176
|
+
// Verify that the extensions value of the statement as elements
|
|
177
|
+
if (
|
|
178
|
+
lastRecord.object.definition.extensions &&
|
|
179
|
+
Object.keys(lastRecord.object.definition.extensions).length
|
|
180
|
+
) {
|
|
181
|
+
const allExtensionsKeys = Object.keys(
|
|
182
|
+
lastRecord.object.definition.extensions
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
//Search for the user-data entry in the extensions
|
|
186
|
+
const userDataExtension = allExtensionsKeys.filter((extension) =>
|
|
187
|
+
extension.includes('user-data')
|
|
188
|
+
)
|
|
189
|
+
userData =
|
|
190
|
+
lastRecord.object.definition.extensions[userDataExtension[0]]
|
|
191
|
+
}
|
|
192
|
+
} else {
|
|
193
|
+
userData = {}
|
|
194
|
+
}
|
|
195
|
+
return userData
|
|
196
|
+
} catch (err) {
|
|
197
|
+
console.log(err)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/*@Description: Helper to The Lesson Status (result) -
|
|
202
|
+
|
|
203
|
+
* @Params {String} email: user email
|
|
204
|
+
* @Params {String} activityId: the IRI string of the activity
|
|
205
|
+
*/
|
|
206
|
+
async _getLessonStatus(email, activityId, verb) {
|
|
207
|
+
let params
|
|
208
|
+
const validVerbs = ['completed', 'suspended']
|
|
209
|
+
if (this.XAPIWrapper) params = this.XAPIWrapper.searchParams()
|
|
210
|
+
let vb = verb && validVerbs.includes(verb) ? verb : 'suspended'
|
|
211
|
+
params['verb'] = this.verbs[vb].id // completed state
|
|
212
|
+
params['activity'] = activityId // this current activity define by this id
|
|
213
|
+
params['agent'] = `{"mbox": "mailto:${email}"}` // the user identify by this email
|
|
214
|
+
params['ascending'] = true //ordered by the oldest statement recorded
|
|
215
|
+
|
|
216
|
+
let completedStmts = await this.XAPIWrapper.getStatements(params)
|
|
217
|
+
let status //placeholder for user data
|
|
218
|
+
if (
|
|
219
|
+
completedStmts &&
|
|
220
|
+
completedStmts.statements &&
|
|
221
|
+
completedStmts.statements.length
|
|
222
|
+
) {
|
|
223
|
+
let lastRecord = completedStmts.statements.toReversed()[0] // get last recorded statement
|
|
224
|
+
|
|
225
|
+
// Verify that result exist
|
|
226
|
+
if (lastRecord.result) status = lastRecord.result
|
|
227
|
+
} else {
|
|
228
|
+
status = {}
|
|
229
|
+
}
|
|
230
|
+
return status
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/* @description: last lesson saved point from the LRS
|
|
234
|
+
* search where the user stopped in the activity
|
|
235
|
+
* return is position in the activity
|
|
236
|
+
* @Params {String} email: user email
|
|
237
|
+
* @Params {String} activityId: the IRI string of the activity
|
|
238
|
+
*/
|
|
239
|
+
|
|
240
|
+
async _getLessonPosition(email, activityId) {
|
|
241
|
+
let params
|
|
242
|
+
let savedPoint = []
|
|
243
|
+
if (this.XAPIWrapper) params = this.XAPIWrapper.searchParams()
|
|
244
|
+
|
|
245
|
+
params['verb'] = this.verbs.terminated.id // completed state
|
|
246
|
+
params['activity'] = activityId // this current activity define by this id
|
|
247
|
+
params['agent'] = `{"mbox": "mailto:${email}"}` // the user identify by this email
|
|
248
|
+
params['ascending'] = true //ordered by the oldest statement recorded
|
|
249
|
+
params['limit'] = 200 //limit of record to return by the LRS
|
|
250
|
+
|
|
251
|
+
let date = new Date()
|
|
252
|
+
let dd = new Date(date.setDate(date.getDate() + 1)).toISOString() //Date is set to next day(tomorrow) for Lrs to take in account statement registered today.
|
|
253
|
+
|
|
254
|
+
params['until'] = dd
|
|
255
|
+
|
|
256
|
+
let progressedStmts = await this.XAPIWrapper.getStatements(params)
|
|
257
|
+
|
|
258
|
+
if (
|
|
259
|
+
progressedStmts &&
|
|
260
|
+
progressedStmts.statements &&
|
|
261
|
+
progressedStmts.statements.length
|
|
262
|
+
) {
|
|
263
|
+
let lastRecord = progressedStmts.statements.toReversed()[0] // get last recorded statement
|
|
264
|
+
// Verify that the extensions value of the statement has elements
|
|
265
|
+
|
|
266
|
+
if (
|
|
267
|
+
lastRecord.object.definition.extensions &&
|
|
268
|
+
Object.keys(lastRecord.object.definition.extensions).length
|
|
269
|
+
) {
|
|
270
|
+
const allExtensionsKeys = Object.keys(
|
|
271
|
+
lastRecord.object.definition.extensions
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
//Search for the user-data entry in the extensions
|
|
275
|
+
savedPoint = allExtensionsKeys.map((extension) => {
|
|
276
|
+
if (extension.includes('ending-point'))
|
|
277
|
+
return lastRecord.object.definition.extensions[extension]
|
|
278
|
+
})
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return savedPoint && savedPoint.length > 0 ? savedPoint[0] : ''
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/* @description: Helper to get the Preferred Settings of the user
|
|
286
|
+
* return is position in the activity
|
|
287
|
+
* @Params {String} email: user email
|
|
288
|
+
* @Params {String} activityId: the IRI string of the activity
|
|
289
|
+
*/
|
|
290
|
+
|
|
291
|
+
async _getPreferredSettings(email, activityId) {
|
|
292
|
+
let params
|
|
293
|
+
let preferredSettings = []
|
|
294
|
+
if (this.XAPIWrapper) params = this.XAPIWrapper.searchParams()
|
|
295
|
+
|
|
296
|
+
params['verb'] = this.verbs.preferred.id // completed state
|
|
297
|
+
params['activity'] = activityId // this current activity define by this id
|
|
298
|
+
params['agent'] = `{"mbox": "mailto:${email}"}` // the user identify by this email
|
|
299
|
+
params['ascending'] = true //ordered by the oldest statement recorded
|
|
300
|
+
params['limit'] = 200 //limit of record to return by the LRS
|
|
301
|
+
|
|
302
|
+
let date = new Date()
|
|
303
|
+
let dd = new Date(date.setDate(date.getDate() + 1)).toISOString() //Date is set to next day(tomorrow) for Lrs to take in account statement registered today.
|
|
304
|
+
|
|
305
|
+
params['until'] = dd
|
|
306
|
+
|
|
307
|
+
let preferredStmts = await this.XAPIWrapper.getStatements(params)
|
|
308
|
+
|
|
309
|
+
if (
|
|
310
|
+
preferredStmts &&
|
|
311
|
+
preferredStmts.statements &&
|
|
312
|
+
preferredStmts.statements.length
|
|
313
|
+
) {
|
|
314
|
+
let lastRecord = preferredStmts.statements.toReversed()[0] // get last recorded statement
|
|
315
|
+
|
|
316
|
+
// Verify that the extensions value of the statement has elements
|
|
317
|
+
if (
|
|
318
|
+
lastRecord.object.definition.extensions &&
|
|
319
|
+
Object.keys(lastRecord.object.definition.extensions).length
|
|
320
|
+
) {
|
|
321
|
+
const allExtensionsKeys = Object.keys(
|
|
322
|
+
lastRecord.object.definition.extensions
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
//Search for the user-data entry in the extensions
|
|
326
|
+
preferredSettings = allExtensionsKeys.map((extension) => {
|
|
327
|
+
if (extension.includes('application-settings'))
|
|
328
|
+
return lastRecord.object.definition.extensions[extension]
|
|
329
|
+
})
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return preferredSettings[0] ? preferredSettings[0] : {}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/* @description: Helper to get the Playbar values of the user
|
|
336
|
+
* @Params {String} email: user email
|
|
337
|
+
* @Params {String} activityId: the IRI string of the activity
|
|
338
|
+
*/
|
|
339
|
+
|
|
340
|
+
async _getPlaybarValues(email, activityId) {
|
|
341
|
+
let params
|
|
342
|
+
let playbarValues = []
|
|
343
|
+
if (this.XAPIWrapper) params = this.XAPIWrapper.searchParams()
|
|
344
|
+
|
|
345
|
+
params['verb'] = this.verbs.played.id // completed state
|
|
346
|
+
params['activity'] = activityId // this current activity define by this id
|
|
347
|
+
params['agent'] = `{"mbox": "mailto:${email}"}` // the user identify by this email
|
|
348
|
+
params['ascending'] = true //ordered by the oldest statement recorded
|
|
349
|
+
params['limit'] = 200 //limit of record to return by the LRS
|
|
350
|
+
|
|
351
|
+
let date = new Date()
|
|
352
|
+
let dd = new Date(date.setDate(date.getDate() + 1)).toISOString() //Date is set to next day(tomorrow) for Lrs to take in account statement registered today.
|
|
353
|
+
|
|
354
|
+
params['until'] = dd
|
|
355
|
+
|
|
356
|
+
let playedStmts = await this.XAPIWrapper.getStatements(params)
|
|
357
|
+
|
|
358
|
+
if (
|
|
359
|
+
playedStmts &&
|
|
360
|
+
playedStmts.statements &&
|
|
361
|
+
playedStmts.statements.length
|
|
362
|
+
) {
|
|
363
|
+
let lastRecord = playedStmts.statements.toReversed()[0] // get last recorded statement
|
|
364
|
+
// Verify that the extensions value of the statement has elements
|
|
365
|
+
if (
|
|
366
|
+
lastRecord.object.definition.extensions &&
|
|
367
|
+
Object.keys(lastRecord.object.definition.extensions).length
|
|
368
|
+
) {
|
|
369
|
+
const allExtensionsKeys = Object.keys(
|
|
370
|
+
lastRecord.object.definition.extensions
|
|
371
|
+
)
|
|
372
|
+
//Search for the user-data entry in the extensions
|
|
373
|
+
playbarValues = allExtensionsKeys.map((extension) => {
|
|
374
|
+
if (extension.includes('playbar-values'))
|
|
375
|
+
return lastRecord.object.definition.extensions[extension]
|
|
376
|
+
})
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return playbarValues[0] ? playbarValues[0] : {}
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Method to get a multiple concurent requests using promiseAll to the server
|
|
383
|
+
* and return all data at once.
|
|
384
|
+
* the following request are made to the server:
|
|
385
|
+
* get application-settings: saved value for the user preferred settings
|
|
386
|
+
* get user-data: saved values for user progression
|
|
387
|
+
* get playbar-value : saved value for the medias play-bar
|
|
388
|
+
* get lesson-status : saved value for the lession status
|
|
389
|
+
*
|
|
390
|
+
* @param {Array} searchParams - Array of Object with key value representing each search paremeter.
|
|
391
|
+
* a parameter object definition is expected to have the following signature:
|
|
392
|
+
* {
|
|
393
|
+
* email{String} agent email,
|
|
394
|
+
* verb {String} verb for the request,
|
|
395
|
+
* activityId {String} - activity id ,
|
|
396
|
+
* }
|
|
397
|
+
* @example here is an exemple of the search param:
|
|
398
|
+
* {email: 'wizardy0@mymailbox.com', verb: 'completed',activityId:'"http://localhost:5173/vuetify_p4/v3"' }
|
|
399
|
+
* @return {object} data with a collection of all the reponses
|
|
400
|
+
* from the promiseAll and the status of last request
|
|
401
|
+
*/
|
|
402
|
+
async _getBulkData(searchParams) {
|
|
403
|
+
const validVerbs = [
|
|
404
|
+
'progressed',
|
|
405
|
+
'completed',
|
|
406
|
+
'suspended',
|
|
407
|
+
'terminated',
|
|
408
|
+
'preferred',
|
|
409
|
+
'played',
|
|
410
|
+
'exited'
|
|
411
|
+
]
|
|
412
|
+
|
|
413
|
+
const paramList = []
|
|
414
|
+
// account statement registered today.
|
|
415
|
+
let date = new Date()
|
|
416
|
+
let dd = new Date(date.setDate(date.getDate() + 1)).toISOString() //Date is set to next day(tomorrow) for Lrs to take in
|
|
417
|
+
|
|
418
|
+
for (let param of searchParams) {
|
|
419
|
+
let { email, verb, activityId } = param
|
|
420
|
+
let vb = verb && validVerbs.includes(verb) ? verb : 'terminated'
|
|
421
|
+
const params = {}
|
|
422
|
+
|
|
423
|
+
params['verb'] = this.verbs[vb].id // completed state
|
|
424
|
+
params['activity'] = activityId // this current activity define by this id
|
|
425
|
+
params['agent'] = `{"mbox": "mailto:${email}"}` // the user identify by this email
|
|
426
|
+
params['ascending'] = true //ordered by the oldest statement recorded
|
|
427
|
+
params['limit'] = 250 //limit of record to return by the LRS
|
|
428
|
+
|
|
429
|
+
params['until'] = dd
|
|
430
|
+
paramList.push(params)
|
|
431
|
+
}
|
|
432
|
+
const { response } = await this.XAPIWrapper.getStatements(paramList)
|
|
433
|
+
|
|
434
|
+
let userData = {},
|
|
435
|
+
playbarValues = {},
|
|
436
|
+
preferredSettings = {},
|
|
437
|
+
lessonStatus = {},
|
|
438
|
+
savedPoint = ''
|
|
439
|
+
|
|
440
|
+
if (response && response.length > 0) {
|
|
441
|
+
for (const res of response) {
|
|
442
|
+
const { statements } = await res.json()
|
|
443
|
+
if (!statements || statements.length == 0) continue
|
|
444
|
+
|
|
445
|
+
let lastRecord = statements.toReversed()[0] // get last recorded statement
|
|
446
|
+
// Verify that the extensions value of the statement has elements
|
|
447
|
+
|
|
448
|
+
//Handeling Lession comppletion status
|
|
449
|
+
if (lastRecord.verb.id.includes('completed')) {
|
|
450
|
+
lessonStatus = lastRecord.result ? lastRecord.result : {}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (
|
|
454
|
+
lastRecord.object.definition.extensions &&
|
|
455
|
+
Object.keys(lastRecord.object.definition.extensions).length
|
|
456
|
+
) {
|
|
457
|
+
const allExtensionsKeys = Object.keys(
|
|
458
|
+
lastRecord.object.definition.extensions
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
//Search for the user-data entry in the extensions
|
|
462
|
+
allExtensionsKeys.forEach((extension) => {
|
|
463
|
+
switch (true) {
|
|
464
|
+
case extension.includes('user-data'): {
|
|
465
|
+
//Search for the user-data entry in the extensions
|
|
466
|
+
const userDataExtension = allExtensionsKeys.filter(
|
|
467
|
+
(extension) => extension.includes('user-data')
|
|
468
|
+
)
|
|
469
|
+
userData =
|
|
470
|
+
lastRecord.object.definition.extensions[userDataExtension[0]]
|
|
471
|
+
|
|
472
|
+
break
|
|
473
|
+
}
|
|
474
|
+
case extension.includes('ending-point'): {
|
|
475
|
+
const userDataExtension = allExtensionsKeys.filter(
|
|
476
|
+
(extension) => extension.includes('ending-point')
|
|
477
|
+
)
|
|
478
|
+
savedPoint =
|
|
479
|
+
lastRecord.object.definition.extensions[userDataExtension[0]]
|
|
480
|
+
|
|
481
|
+
break
|
|
482
|
+
}
|
|
483
|
+
case extension.includes('application-settings'): {
|
|
484
|
+
const userDataExtension = allExtensionsKeys.filter(
|
|
485
|
+
(extension) => extension.includes('application-settings')
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
const settingsData =
|
|
489
|
+
lastRecord.object.definition.extensions[userDataExtension[0]]
|
|
490
|
+
|
|
491
|
+
preferredSettings = settingsData ? settingsData : {}
|
|
492
|
+
|
|
493
|
+
break
|
|
494
|
+
}
|
|
495
|
+
case extension.includes('playbar-values'): {
|
|
496
|
+
const userDataExtension = allExtensionsKeys.filter(
|
|
497
|
+
(extension) => extension.includes('playbar-values')
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
const playbarData =
|
|
501
|
+
lastRecord.object.definition.extensions[userDataExtension[0]]
|
|
502
|
+
playbarValues = playbarData ? playbarData : {}
|
|
503
|
+
|
|
504
|
+
break
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
})
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
return {
|
|
513
|
+
userData,
|
|
514
|
+
savedPoint,
|
|
515
|
+
preferredSettings,
|
|
516
|
+
playbarValues,
|
|
517
|
+
lessonStatus
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|