@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;
@@ -92,7 +92,7 @@ export const accountsGateway = (initializer) => (configProvider) => {
92
92
  accountsUuid: user.uuid,
93
93
  });
94
94
  }
95
- return { data, fullName: user.full_name, platformUserId, uuid: user.uuid };
95
+ return { data, firstName: user.first_name, lastName: user.last_name, fullName: user.full_name, platformUserId, uuid: user.uuid };
96
96
  });
97
97
  return results.filter(isDefined);
98
98
  };
@@ -1,14 +1,9 @@
1
1
  import formatISO from 'date-fns/formatISO';
2
2
  import { v4 as uuid } from 'uuid';
3
+ import { formatAgent } from './attempt-utils';
3
4
  export const addStatementDefaultFields = (statement, user) => ({
4
5
  id: uuid(),
5
- actor: {
6
- account: {
7
- homePage: 'https://openstax.org',
8
- name: user.uuid,
9
- },
10
- objectType: 'Agent',
11
- },
6
+ actor: formatAgent(user.uuid),
12
7
  timestamp: formatISO(new Date()),
13
8
  ...statement,
14
9
  });
@@ -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>;
@@ -11,6 +11,18 @@ import intervalToDuration from 'date-fns/intervalToDuration';
11
11
  import isAfter from 'date-fns/isAfter';
12
12
  import isBefore from 'date-fns/isBefore';
13
13
  import parseISO from 'date-fns/parseISO';
14
+ export const formatAgent = (agent) => {
15
+ if (typeof agent === 'string') {
16
+ return {
17
+ objectType: 'Agent',
18
+ account: {
19
+ homePage: 'https://openstax.org',
20
+ name: agent,
21
+ },
22
+ };
23
+ }
24
+ return agent;
25
+ };
14
26
  var Verb;
15
27
  (function (Verb) {
16
28
  Verb["Attempted"] = "http://adlnet.gov/expapi/verbs/attempted";
@@ -53,9 +65,12 @@ export const resolveScoredForAttempt = (statements, attempt, activityIRI) => {
53
65
  return mostRecentStatement(scoringStatements);
54
66
  };
55
67
  const resolveAttemptEntries = (statements, attempts, activityIRI) => attempts.reduce((acc, attempt) => {
68
+ var _a, _b, _c, _d;
56
69
  const completed = resolveCompletedForAttempt(statements, attempt, activityIRI);
57
70
  const scored = resolveScoredForAttempt(statements, attempt, activityIRI);
58
- acc.push({ attempt, completed, scored });
71
+ 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[EXT_PENDING_SCORING]) === 'true' && scored === undefined) ||
72
+ ((_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[EXT_PENDING_SCORING]) === 'true';
73
+ acc.push({ attempt, completed, scored, needsScoring });
59
74
  return acc;
60
75
  }, []);
61
76
  export const getStatementTimeString = (statement) => 'stored' in statement && statement.stored ? statement.stored : statement.timestamp;
@@ -150,7 +165,7 @@ export const loadActivityAttemptInfo = async (gateway, activityIRI, options) =>
150
165
  const loadOptions = parentActivityAttempt ? { ...partialOptions, registration: parentActivityAttempt } : partialOptions;
151
166
  return resolveAttemptInfo(await gateway.getAllXapiStatements({ ...loadOptions, activity: activityIRI }), { ...options, activityIRI });
152
167
  };
153
- export const createStatement = (verb, activity, attempt, parentActivityIRI) => {
168
+ export const createStatement = (verb, activity, attempt, parentActivityIRI, instructor) => {
154
169
  return {
155
170
  context: {
156
171
  ...(parentActivityIRI ? {
@@ -164,6 +179,7 @@ export const createStatement = (verb, activity, attempt, parentActivityIRI) => {
164
179
  },
165
180
  } : {}),
166
181
  registration: attempt,
182
+ ...(instructor ? { instructor: formatAgent(instructor) } : {}),
167
183
  },
168
184
  object: {
169
185
  definition: {
@@ -207,7 +223,7 @@ export const createAttemptStatement = (activity, parentActivity) => {
207
223
  } : {}),
208
224
  ...(parentActivity.attempt ? {
209
225
  registration: parentActivity.attempt,
210
- } : {})
226
+ } : {}),
211
227
  },
212
228
  } : {}),
213
229
  object: {
@@ -273,7 +289,7 @@ export const createCompletedStatement = (attemptStatement, result) => {
273
289
  statement: {
274
290
  objectType: 'StatementRef',
275
291
  id: attemptStatement.id,
276
- }
292
+ },
277
293
  },
278
294
  object: attemptStatement.object,
279
295
  verb: {
@@ -301,7 +317,7 @@ export const putCompletedPendingScoringStatement = async (gateway, attemptStatem
301
317
  ], user))[0];
302
318
  };
303
319
  // scored statement for when the open written response has been graded.
304
- export const createScoredStatement = (attemptStatement, result) => {
320
+ export const createScoredStatement = (attemptStatement, result, instructor) => {
305
321
  var _a, _b;
306
322
  return {
307
323
  context: {
@@ -314,7 +330,8 @@ export const createScoredStatement = (attemptStatement, result) => {
314
330
  statement: {
315
331
  objectType: 'StatementRef',
316
332
  id: attemptStatement.id,
317
- }
333
+ },
334
+ ...(instructor ? { instructor: formatAgent(instructor) } : {}),
318
335
  },
319
336
  object: attemptStatement.object,
320
337
  verb: {
@@ -327,6 +344,16 @@ export const createScoredStatement = (attemptStatement, result) => {
327
344
  }
328
345
  };
329
346
  };
330
- export const putScoredStatement = async (gateway, attemptStatement, result, user) => {
331
- return (await gateway.putXapiStatements([createScoredStatement(attemptStatement, result)], user))[0];
347
+ export const putScoredStatement = async (gateway, attemptStatement, result, user, instructor) => {
348
+ return (await gateway.putXapiStatements([createScoredStatement(attemptStatement, result, instructor)], user))[0];
349
+ };
350
+ // scored statement for retry assessments - records a score but marks the attempt as still pending scoring
351
+ export const createScoredPendingScoringStatement = (attemptStatement, result) => createScoredStatement(attemptStatement, {
352
+ ...result,
353
+ extensions: { [EXT_PENDING_SCORING]: 'true' },
354
+ });
355
+ export const putScoredPendingScoringStatement = async (gateway, attemptStatement, result, user) => {
356
+ return (await gateway.putXapiStatements([
357
+ createScoredPendingScoringStatement(attemptStatement, result),
358
+ ], user))[0];
332
359
  };
@@ -7,6 +7,7 @@ import { resolveConfigValue } from '../../config';
7
7
  import { UnauthorizedError } from '../../errors';
8
8
  import { ifDefined } from '../../guards';
9
9
  import { hashValue } from '../../misc/hashValue';
10
+ import { formatAgent } from './attempt-utils';
10
11
  const pageSize = 5;
11
12
  export const fileSystemLrsGateway = (initializer) => (configProvider) => ({ authProvider }) => {
12
13
  const name = resolveConfigValue(configProvider[initializer.configSpace || 'fileSystem'].name);
@@ -69,13 +70,7 @@ export const fileSystemLrsGateway = (initializer) => (configProvider) => ({ auth
69
70
  const statementsWithDefaults = statements.map(statement => ({
70
71
  ...statement,
71
72
  id: uuid(),
72
- actor: {
73
- account: {
74
- homePage: 'https://openstax.org',
75
- name: author.uuid,
76
- },
77
- objectType: 'Agent',
78
- },
73
+ actor: formatAgent(author.uuid),
79
74
  timestamp: formatISO(new Date()),
80
75
  }));
81
76
  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?: {
@@ -7,6 +7,7 @@ import { ifDefined } from '../../guards';
7
7
  import { retryWithDelay } from '../../misc/helpers';
8
8
  import { METHOD } from '../../routing';
9
9
  import { addStatementDefaultFields } from './addStatementDefaultFields';
10
+ import { formatAgent } from './attempt-utils';
10
11
  import { RequestBatcher } from './batching';
11
12
  export const lrsGateway = (initializer) => (configProvider) => {
12
13
  const config = configProvider[ifDefined(initializer.configSpace, 'lrs')];
@@ -33,22 +34,6 @@ export const lrsGateway = (initializer) => (configProvider) => {
33
34
  const makeFetch = async (options) => {
34
35
  return enableBatching ? batcher.queueRequest(options) : batcher.singleRequest(options);
35
36
  };
36
- /**
37
- * Formats an agent parameter into a full XapiAgent object.
38
- * Accepts either a UUID string or a full XapiAgent object.
39
- */
40
- const formatAgent = (agent) => {
41
- if (typeof agent === 'string') {
42
- return {
43
- objectType: 'Agent',
44
- account: {
45
- homePage: 'https://openstax.org',
46
- name: agent,
47
- },
48
- };
49
- }
50
- return agent;
51
- };
52
37
  // Note: This method actually uses POST
53
38
  const putXapiStatements = async (statements, user) => {
54
39
  const userObj = user
@@ -110,13 +95,7 @@ ${await response.text()}`);
110
95
  queryParams.until = options.until;
111
96
  // Add agent unless anyUser is true
112
97
  if (anyUser !== true) {
113
- queryParams.agent = JSON.stringify({
114
- account: {
115
- homePage: 'https://openstax.org',
116
- name: user || assertDefined(await authProvider.getUser(), new UnauthorizedError()).uuid,
117
- },
118
- objectType: 'Agent',
119
- });
98
+ queryParams.agent = JSON.stringify(formatAgent(user || assertDefined(await authProvider.getUser(), new UnauthorizedError()).uuid));
120
99
  }
121
100
  return makeFetch({
122
101
  path: '/data/xAPI/statements',
@@ -1,6 +1,6 @@
1
1
  import partition from 'lodash/fp/partition';
2
2
  import { roundToPrecision } from '../..';
3
- import { EXT_PENDING_SCORING, getStatementTimeString, resolveAttemptInfo } from './attempt-utils';
3
+ import { getStatementTimeString, resolveAttemptInfo } from './attempt-utils';
4
4
  export const getRegistrationAttemptInfo = async (lrs, registration, options) => {
5
5
  const { currentPreference, ...xapiOptions } = options !== null && options !== void 0 ? options : {};
6
6
  const allStatements = await lrs.getAllXapiStatements({ ...xapiOptions, registration });
@@ -24,7 +24,7 @@ export const getRegistrationAttemptInfo = async (lrs, registration, options) =>
24
24
  // ltijs: https://cvmcosta.me/ltijs/#/grading
25
25
  // Note: "min" is currently completely ignored
26
26
  export const getScoreGrade = (entry, options) => {
27
- var _a, _b, _c, _d, _e;
27
+ var _a, _b;
28
28
  const { raw, scaled, max } = ((_b = (_a = entry.scored) === null || _a === void 0 ? void 0 : _a.result) === null || _b === void 0 ? void 0 : _b.score) || {};
29
29
  const { maxScore, userId } = options;
30
30
  const scoreMaximum = maxScore !== null && maxScore !== void 0 ? maxScore : 100;
@@ -37,8 +37,7 @@ export const getScoreGrade = (entry, options) => {
37
37
  startedAt: entry.attempt ? getStatementTimeString(entry.attempt) : undefined,
38
38
  submittedAt: entry.completed ? getStatementTimeString(entry.completed) : undefined,
39
39
  };
40
- const pendingManual = entry.scored === undefined
41
- && ((_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[EXT_PENDING_SCORING]) === 'true';
40
+ const pendingManual = entry.needsScoring;
42
41
  return {
43
42
  userId,
44
43
  activityProgress: entry.completed ? 'Completed' : 'Started',