@openstax/ts-utils 1.44.0 → 1.44.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.
@@ -66,6 +66,8 @@ export type SearchUsersResponse = {
66
66
  export type UpdateUserPayload = Partial<ApiUser>;
67
67
  export type MappedUserInfo<T> = {
68
68
  data: T;
69
+ firstName: string;
70
+ lastName: string;
69
71
  fullName: string;
70
72
  platformUserId?: string;
71
73
  uuid: string;
@@ -81,6 +83,8 @@ export declare const accountsGateway: <C extends string = "accounts">(initialize
81
83
  [uuid: string]: T;
82
84
  }, platformId?: string) => Promise<{
83
85
  data: T;
86
+ firstName: string;
87
+ lastName: string;
84
88
  fullName: string;
85
89
  platformUserId: string | undefined;
86
90
  uuid: string;
@@ -98,7 +98,7 @@ const accountsGateway = (initializer) => (configProvider) => {
98
98
  accountsUuid: user.uuid,
99
99
  });
100
100
  }
101
- return { data, fullName: user.full_name, platformUserId, uuid: user.uuid };
101
+ return { data, firstName: user.first_name, lastName: user.last_name, fullName: user.full_name, platformUserId, uuid: user.uuid };
102
102
  });
103
103
  return results.filter(guards_1.isDefined);
104
104
  };
@@ -6,15 +6,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.addStatementDefaultFields = void 0;
7
7
  const formatISO_1 = __importDefault(require("date-fns/formatISO"));
8
8
  const uuid_1 = require("uuid");
9
+ const attempt_utils_1 = require("./attempt-utils");
9
10
  const addStatementDefaultFields = (statement, user) => ({
10
11
  id: (0, uuid_1.v4)(),
11
- actor: {
12
- account: {
13
- homePage: 'https://openstax.org',
14
- name: user.uuid,
15
- },
16
- objectType: 'Agent',
17
- },
12
+ actor: (0, attempt_utils_1.formatAgent)(user.uuid),
18
13
  timestamp: (0, formatISO_1.default)(new Date()),
19
14
  ...statement,
20
15
  });
@@ -1,9 +1,11 @@
1
- import { LrsGateway, UXapiStatement } from '.';
1
+ import { LrsGateway, UXapiStatement, XapiAgent } from '.';
2
+ export declare const formatAgent: (agent: string | XapiAgent) => XapiAgent;
2
3
  export declare const EXT_PENDING_SCORING = "https://openstax.org/xapi/extensions/pending-scoring";
3
4
  export type AttemptEntry = {
4
5
  attempt: UXapiStatement;
5
6
  completed?: UXapiStatement;
6
7
  scored?: UXapiStatement;
8
+ needsScoring: boolean;
7
9
  };
8
10
  export type ActivityState = {
9
11
  attempts: number;
@@ -53,7 +55,7 @@ export declare const createStatement: (verb: UXapiStatement["verb"], activity: {
53
55
  extensions?: {
54
56
  [key: string]: string;
55
57
  };
56
- }, attempt: string, parentActivityIRI?: string) => Pick<UXapiStatement, "object" | "verb" | "context">;
58
+ }, attempt: string, parentActivityIRI?: string, instructor?: string) => Pick<UXapiStatement, "object" | "verb" | "context">;
57
59
  export declare const createAttemptStatement: (activity: {
58
60
  iri: string;
59
61
  type: string;
@@ -82,5 +84,7 @@ export declare const createCompletedStatement: (attemptStatement: UXapiStatement
82
84
  export declare const putCompletedStatement: (gateway: LrsGateway, attemptStatement: UXapiStatement, result: UXapiStatement["result"], user?: string) => Promise<import(".").EagerXapiStatement>;
83
85
  export declare const createCompletedPendingScoringStatement: (attemptStatement: UXapiStatement) => Pick<UXapiStatement, "object" | "verb" | "context" | "result">;
84
86
  export declare const putCompletedPendingScoringStatement: (gateway: LrsGateway, attemptStatement: UXapiStatement, user?: string) => Promise<import(".").EagerXapiStatement>;
85
- export declare const createScoredStatement: (attemptStatement: UXapiStatement, result: UXapiStatement["result"]) => Pick<UXapiStatement, "object" | "verb" | "context" | "result">;
86
- export declare const putScoredStatement: (gateway: LrsGateway, attemptStatement: UXapiStatement, result: UXapiStatement["result"], user?: string) => Promise<import(".").EagerXapiStatement>;
87
+ export declare const createScoredStatement: (attemptStatement: UXapiStatement, result: UXapiStatement["result"], instructor?: string) => Pick<UXapiStatement, "object" | "verb" | "context" | "result">;
88
+ export declare const putScoredStatement: (gateway: LrsGateway, attemptStatement: UXapiStatement, result: UXapiStatement["result"], user?: string, instructor?: string) => Promise<import(".").EagerXapiStatement>;
89
+ export declare const createScoredPendingScoringStatement: (attemptStatement: UXapiStatement, result: UXapiStatement["result"]) => Pick<UXapiStatement, "object" | "verb" | "context" | "result">;
90
+ export declare const putScoredPendingScoringStatement: (gateway: LrsGateway, attemptStatement: UXapiStatement, result: UXapiStatement["result"], user?: string) => Promise<import(".").EagerXapiStatement>;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.putScoredStatement = exports.createScoredStatement = exports.putCompletedPendingScoringStatement = exports.createCompletedPendingScoringStatement = exports.putCompletedStatement = exports.createCompletedStatement = exports.putAttemptActivityStatement = exports.createAttemptActivityStatement = exports.putAttemptStatement = exports.createAttemptStatement = exports.createStatement = exports.loadActivityAttemptInfo = exports.loadStatementsForActivityAndFirstChildren = exports.resolveAttemptInfo = exports.bestEntry = exports.mostRecentEntry = exports.oldestEntry = exports.mostRecentStatement = exports.oldestStatement = exports.getStatementTime = exports.getStatementTimeString = exports.resolveScoredForAttempt = exports.resolveCompletedForAttempt = exports.resolveAttempts = exports.matchAttemptScored = exports.matchAttemptCompleted = exports.matchAttempt = exports.EXT_PENDING_SCORING = void 0;
6
+ exports.putScoredPendingScoringStatement = exports.createScoredPendingScoringStatement = exports.putScoredStatement = exports.createScoredStatement = exports.putCompletedPendingScoringStatement = exports.createCompletedPendingScoringStatement = exports.putCompletedStatement = exports.createCompletedStatement = exports.putAttemptActivityStatement = exports.createAttemptActivityStatement = exports.putAttemptStatement = exports.createAttemptStatement = exports.createStatement = exports.loadActivityAttemptInfo = exports.loadStatementsForActivityAndFirstChildren = exports.resolveAttemptInfo = exports.bestEntry = exports.mostRecentEntry = exports.oldestEntry = exports.mostRecentStatement = exports.oldestStatement = exports.getStatementTime = exports.getStatementTimeString = exports.resolveScoredForAttempt = exports.resolveCompletedForAttempt = exports.resolveAttempts = exports.matchAttemptScored = exports.matchAttemptCompleted = exports.matchAttempt = exports.EXT_PENDING_SCORING = exports.formatAgent = void 0;
7
7
  /*
8
8
  * the structure of xapi statements for handling multiple attempts of an activity
9
9
  * including the option of a parent activity/attempt such that a new attempt
@@ -17,6 +17,19 @@ const intervalToDuration_1 = __importDefault(require("date-fns/intervalToDuratio
17
17
  const isAfter_1 = __importDefault(require("date-fns/isAfter"));
18
18
  const isBefore_1 = __importDefault(require("date-fns/isBefore"));
19
19
  const parseISO_1 = __importDefault(require("date-fns/parseISO"));
20
+ const formatAgent = (agent) => {
21
+ if (typeof agent === 'string') {
22
+ return {
23
+ objectType: 'Agent',
24
+ account: {
25
+ homePage: 'https://openstax.org',
26
+ name: agent,
27
+ },
28
+ };
29
+ }
30
+ return agent;
31
+ };
32
+ exports.formatAgent = formatAgent;
20
33
  var Verb;
21
34
  (function (Verb) {
22
35
  Verb["Attempted"] = "http://adlnet.gov/expapi/verbs/attempted";
@@ -65,9 +78,12 @@ const resolveScoredForAttempt = (statements, attempt, activityIRI) => {
65
78
  };
66
79
  exports.resolveScoredForAttempt = resolveScoredForAttempt;
67
80
  const resolveAttemptEntries = (statements, attempts, activityIRI) => attempts.reduce((acc, attempt) => {
81
+ var _a, _b, _c, _d;
68
82
  const completed = (0, exports.resolveCompletedForAttempt)(statements, attempt, activityIRI);
69
83
  const scored = (0, exports.resolveScoredForAttempt)(statements, attempt, activityIRI);
70
- acc.push({ attempt, completed, scored });
84
+ const needsScoring = (((_b = (_a = completed === null || completed === void 0 ? void 0 : completed.result) === null || _a === void 0 ? void 0 : _a.extensions) === null || _b === void 0 ? void 0 : _b[exports.EXT_PENDING_SCORING]) === 'true' && scored === undefined) ||
85
+ ((_d = (_c = scored === null || scored === void 0 ? void 0 : scored.result) === null || _c === void 0 ? void 0 : _c.extensions) === null || _d === void 0 ? void 0 : _d[exports.EXT_PENDING_SCORING]) === 'true';
86
+ acc.push({ attempt, completed, scored, needsScoring });
71
87
  return acc;
72
88
  }, []);
73
89
  const getStatementTimeString = (statement) => 'stored' in statement && statement.stored ? statement.stored : statement.timestamp;
@@ -167,7 +183,7 @@ const loadActivityAttemptInfo = async (gateway, activityIRI, options) => {
167
183
  return (0, exports.resolveAttemptInfo)(await gateway.getAllXapiStatements({ ...loadOptions, activity: activityIRI }), { ...options, activityIRI });
168
184
  };
169
185
  exports.loadActivityAttemptInfo = loadActivityAttemptInfo;
170
- const createStatement = (verb, activity, attempt, parentActivityIRI) => {
186
+ const createStatement = (verb, activity, attempt, parentActivityIRI, instructor) => {
171
187
  return {
172
188
  context: {
173
189
  ...(parentActivityIRI ? {
@@ -181,6 +197,7 @@ const createStatement = (verb, activity, attempt, parentActivityIRI) => {
181
197
  },
182
198
  } : {}),
183
199
  registration: attempt,
200
+ ...(instructor ? { instructor: (0, exports.formatAgent)(instructor) } : {}),
184
201
  },
185
202
  object: {
186
203
  definition: {
@@ -225,7 +242,7 @@ const createAttemptStatement = (activity, parentActivity) => {
225
242
  } : {}),
226
243
  ...(parentActivity.attempt ? {
227
244
  registration: parentActivity.attempt,
228
- } : {})
245
+ } : {}),
229
246
  },
230
247
  } : {}),
231
248
  object: {
@@ -295,7 +312,7 @@ const createCompletedStatement = (attemptStatement, result) => {
295
312
  statement: {
296
313
  objectType: 'StatementRef',
297
314
  id: attemptStatement.id,
298
- }
315
+ },
299
316
  },
300
317
  object: attemptStatement.object,
301
318
  verb: {
@@ -327,7 +344,7 @@ const putCompletedPendingScoringStatement = async (gateway, attemptStatement, us
327
344
  };
328
345
  exports.putCompletedPendingScoringStatement = putCompletedPendingScoringStatement;
329
346
  // scored statement for when the open written response has been graded.
330
- const createScoredStatement = (attemptStatement, result) => {
347
+ const createScoredStatement = (attemptStatement, result, instructor) => {
331
348
  var _a, _b;
332
349
  return {
333
350
  context: {
@@ -340,7 +357,8 @@ const createScoredStatement = (attemptStatement, result) => {
340
357
  statement: {
341
358
  objectType: 'StatementRef',
342
359
  id: attemptStatement.id,
343
- }
360
+ },
361
+ ...(instructor ? { instructor: (0, exports.formatAgent)(instructor) } : {}),
344
362
  },
345
363
  object: attemptStatement.object,
346
364
  verb: {
@@ -354,7 +372,19 @@ const createScoredStatement = (attemptStatement, result) => {
354
372
  };
355
373
  };
356
374
  exports.createScoredStatement = createScoredStatement;
357
- const putScoredStatement = async (gateway, attemptStatement, result, user) => {
358
- return (await gateway.putXapiStatements([(0, exports.createScoredStatement)(attemptStatement, result)], user))[0];
375
+ const putScoredStatement = async (gateway, attemptStatement, result, user, instructor) => {
376
+ return (await gateway.putXapiStatements([(0, exports.createScoredStatement)(attemptStatement, result, instructor)], user))[0];
359
377
  };
360
378
  exports.putScoredStatement = putScoredStatement;
379
+ // scored statement for retry assessments - records a score but marks the attempt as still pending scoring
380
+ const createScoredPendingScoringStatement = (attemptStatement, result) => (0, exports.createScoredStatement)(attemptStatement, {
381
+ ...result,
382
+ extensions: { [exports.EXT_PENDING_SCORING]: 'true' },
383
+ });
384
+ exports.createScoredPendingScoringStatement = createScoredPendingScoringStatement;
385
+ const putScoredPendingScoringStatement = async (gateway, attemptStatement, result, user) => {
386
+ return (await gateway.putXapiStatements([
387
+ (0, exports.createScoredPendingScoringStatement)(attemptStatement, result),
388
+ ], user))[0];
389
+ };
390
+ exports.putScoredPendingScoringStatement = putScoredPendingScoringStatement;
@@ -46,6 +46,7 @@ const config_1 = require("../../config");
46
46
  const errors_1 = require("../../errors");
47
47
  const guards_1 = require("../../guards");
48
48
  const hashValue_1 = require("../../misc/hashValue");
49
+ const attempt_utils_1 = require("./attempt-utils");
49
50
  const pageSize = 5;
50
51
  const fileSystemLrsGateway = (initializer) => (configProvider) => ({ authProvider }) => {
51
52
  const name = (0, config_1.resolveConfigValue)(configProvider[initializer.configSpace || 'fileSystem'].name);
@@ -108,13 +109,7 @@ const fileSystemLrsGateway = (initializer) => (configProvider) => ({ authProvide
108
109
  const statementsWithDefaults = statements.map(statement => ({
109
110
  ...statement,
110
111
  id: (0, uuid_1.v4)(),
111
- actor: {
112
- account: {
113
- homePage: 'https://openstax.org',
114
- name: author.uuid,
115
- },
116
- objectType: 'Agent',
117
- },
112
+ actor: (0, attempt_utils_1.formatAgent)(author.uuid),
118
113
  timestamp: (0, formatISO_1.default)(new Date()),
119
114
  }));
120
115
  await load;
@@ -23,13 +23,7 @@ export interface StateDocument {
23
23
  [key: string]: any;
24
24
  }
25
25
  export interface XapiStatement {
26
- actor: {
27
- account: {
28
- homePage: 'https://openstax.org';
29
- name: string;
30
- };
31
- objectType: 'Agent';
32
- };
26
+ actor: XapiAgent;
33
27
  id: string;
34
28
  context?: {
35
29
  contextActivities?: {
@@ -46,6 +40,7 @@ export interface XapiStatement {
46
40
  };
47
41
  registration?: string;
48
42
  platform?: string;
43
+ instructor?: XapiAgent;
49
44
  };
50
45
  object: {
51
46
  definition?: {
@@ -10,6 +10,7 @@ const guards_1 = require("../../guards");
10
10
  const helpers_1 = require("../../misc/helpers");
11
11
  const routing_1 = require("../../routing");
12
12
  const addStatementDefaultFields_1 = require("./addStatementDefaultFields");
13
+ const attempt_utils_1 = require("./attempt-utils");
13
14
  const batching_1 = require("./batching");
14
15
  const lrsGateway = (initializer) => (configProvider) => {
15
16
  const config = configProvider[(0, guards_1.ifDefined)(initializer.configSpace, 'lrs')];
@@ -36,22 +37,6 @@ const lrsGateway = (initializer) => (configProvider) => {
36
37
  const makeFetch = async (options) => {
37
38
  return enableBatching ? batcher.queueRequest(options) : batcher.singleRequest(options);
38
39
  };
39
- /**
40
- * Formats an agent parameter into a full XapiAgent object.
41
- * Accepts either a UUID string or a full XapiAgent object.
42
- */
43
- const formatAgent = (agent) => {
44
- if (typeof agent === 'string') {
45
- return {
46
- objectType: 'Agent',
47
- account: {
48
- homePage: 'https://openstax.org',
49
- name: agent,
50
- },
51
- };
52
- }
53
- return agent;
54
- };
55
40
  // Note: This method actually uses POST
56
41
  const putXapiStatements = async (statements, user) => {
57
42
  const userObj = user
@@ -113,13 +98,7 @@ ${await response.text()}`);
113
98
  queryParams.until = options.until;
114
99
  // Add agent unless anyUser is true
115
100
  if (anyUser !== true) {
116
- queryParams.agent = JSON.stringify({
117
- account: {
118
- homePage: 'https://openstax.org',
119
- name: user || (0, assertions_1.assertDefined)(await authProvider.getUser(), new errors_1.UnauthorizedError()).uuid,
120
- },
121
- objectType: 'Agent',
122
- });
101
+ queryParams.agent = JSON.stringify((0, attempt_utils_1.formatAgent)(user || (0, assertions_1.assertDefined)(await authProvider.getUser(), new errors_1.UnauthorizedError()).uuid));
123
102
  }
124
103
  return makeFetch({
125
104
  path: '/data/xAPI/statements',
@@ -169,7 +148,7 @@ ${await response.text()}`);
169
148
  * @returns The state document as a JSON object, or null if not found
170
149
  */
171
150
  const getState = async (activityId, agent, stateId, registration) => {
172
- const agentObj = formatAgent(agent);
151
+ const agentObj = (0, attempt_utils_1.formatAgent)(agent);
173
152
  const queryParams = {
174
153
  activityId,
175
154
  agent: JSON.stringify(agentObj),
@@ -207,7 +186,7 @@ ${await response.text()}`);
207
186
  * @param registration - Optional registration UUID
208
187
  */
209
188
  const setState = async (activityId, agent, stateId, body, registration) => {
210
- const agentObj = formatAgent(agent);
189
+ const agentObj = (0, attempt_utils_1.formatAgent)(agent);
211
190
  const queryParams = {
212
191
  activityId,
213
192
  agent: JSON.stringify(agentObj),
@@ -31,7 +31,7 @@ exports.getRegistrationAttemptInfo = getRegistrationAttemptInfo;
31
31
  // ltijs: https://cvmcosta.me/ltijs/#/grading
32
32
  // Note: "min" is currently completely ignored
33
33
  const getScoreGrade = (entry, options) => {
34
- var _a, _b, _c, _d, _e;
34
+ var _a, _b;
35
35
  const { raw, scaled, max } = ((_b = (_a = entry.scored) === null || _a === void 0 ? void 0 : _a.result) === null || _b === void 0 ? void 0 : _b.score) || {};
36
36
  const { maxScore, userId } = options;
37
37
  const scoreMaximum = maxScore !== null && maxScore !== void 0 ? maxScore : 100;
@@ -44,8 +44,7 @@ const getScoreGrade = (entry, options) => {
44
44
  startedAt: entry.attempt ? (0, attempt_utils_1.getStatementTimeString)(entry.attempt) : undefined,
45
45
  submittedAt: entry.completed ? (0, attempt_utils_1.getStatementTimeString)(entry.completed) : undefined,
46
46
  };
47
- const pendingManual = entry.scored === undefined
48
- && ((_e = (_d = (_c = entry.completed) === null || _c === void 0 ? void 0 : _c.result) === null || _d === void 0 ? void 0 : _d.extensions) === null || _e === void 0 ? void 0 : _e[attempt_utils_1.EXT_PENDING_SCORING]) === 'true';
47
+ const pendingManual = entry.needsScoring;
49
48
  return {
50
49
  userId,
51
50
  activityProgress: entry.completed ? 'Completed' : 'Started',