@openstax/ts-utils 1.37.1 → 1.38.0-beta
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/dist/cjs/services/lrsGateway/attempt-utils.d.ts +15 -6
- package/dist/cjs/services/lrsGateway/attempt-utils.js +125 -33
- package/dist/cjs/services/lrsGateway/xapiUtils.js +23 -22
- package/dist/cjs/tsconfig.without-specs.cjs.tsbuildinfo +1 -1
- package/dist/esm/services/lrsGateway/attempt-utils.d.ts +15 -6
- package/dist/esm/services/lrsGateway/attempt-utils.js +118 -32
- package/dist/esm/services/lrsGateway/xapiUtils.js +24 -23
- package/dist/esm/tsconfig.without-specs.esm.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/script/bin/.init-params-script.bash.swp +0 -0
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
import { LrsGateway, UXapiStatement } from '.';
|
|
2
|
+
export declare const EXT_PENDING_SCORING = "https://openstax.org/xapi/extensions/pending-scoring";
|
|
3
|
+
export type AttemptEntry = {
|
|
4
|
+
attempt: UXapiStatement;
|
|
5
|
+
completed?: UXapiStatement;
|
|
6
|
+
scored?: UXapiStatement;
|
|
7
|
+
};
|
|
2
8
|
export type ActivityState = {
|
|
3
9
|
attempts: number;
|
|
4
|
-
bestCompletedAttempt?: UXapiStatement;
|
|
5
|
-
bestCompletedAttemptCompleted?: UXapiStatement;
|
|
6
10
|
completedAttempts: number;
|
|
7
|
-
currentAttempt?: UXapiStatement;
|
|
8
|
-
currentAttemptCompleted?: UXapiStatement;
|
|
9
11
|
currentAttemptStatements: UXapiStatement[];
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
current?: AttemptEntry;
|
|
13
|
+
best?: AttemptEntry;
|
|
14
|
+
mostRecentWithCompleted?: AttemptEntry;
|
|
12
15
|
};
|
|
13
16
|
export declare const matchAttempt: (statement: UXapiStatement) => boolean;
|
|
14
17
|
export declare const matchAttemptCompleted: (attempt: UXapiStatement) => (statement: UXapiStatement) => boolean;
|
|
18
|
+
export declare const matchAttemptScored: (attempt: UXapiStatement) => (statement: UXapiStatement) => boolean;
|
|
15
19
|
export declare const resolveAttempts: (statements: UXapiStatement[], options?: {
|
|
16
20
|
activityIRI?: string;
|
|
17
21
|
parentActivityAttempt?: string;
|
|
18
22
|
}) => UXapiStatement[];
|
|
19
23
|
export declare const resolveCompletedForAttempt: (statements: UXapiStatement[], attempt: UXapiStatement, activityIRI?: string) => UXapiStatement | undefined;
|
|
24
|
+
export declare const resolveScoredForAttempt: (statements: UXapiStatement[], attempt: UXapiStatement, activityIRI?: string) => UXapiStatement | undefined;
|
|
20
25
|
export declare const oldestStatement: (statements: UXapiStatement[]) => UXapiStatement | undefined;
|
|
21
26
|
export declare const mostRecentStatement: (statements: UXapiStatement[]) => UXapiStatement | undefined;
|
|
22
27
|
export declare const resolveAttemptInfo: (statements: UXapiStatement[], options?: {
|
|
@@ -70,3 +75,7 @@ export declare const createAttemptActivityStatement: (attemptStatement: UXapiSta
|
|
|
70
75
|
export declare const putAttemptActivityStatement: (gateway: LrsGateway, attemptStatement: UXapiStatement, verb: UXapiStatement["verb"], result?: UXapiStatement["result"]) => Promise<import(".").EagerXapiStatement>;
|
|
71
76
|
export declare const createCompletedStatement: (attemptStatement: UXapiStatement, result?: UXapiStatement["result"]) => Pick<UXapiStatement, "object" | "verb" | "context" | "result">;
|
|
72
77
|
export declare const putCompletedStatement: (gateway: LrsGateway, attemptStatement: UXapiStatement, result: UXapiStatement["result"]) => Promise<import(".").EagerXapiStatement>;
|
|
78
|
+
export declare const createCompletedPendingScoringStatement: (attemptStatement: UXapiStatement) => Pick<UXapiStatement, "object" | "verb" | "context" | "result">;
|
|
79
|
+
export declare const putCompletedPendingScoringStatement: (gateway: LrsGateway, attemptStatement: UXapiStatement) => Promise<import(".").EagerXapiStatement>;
|
|
80
|
+
export declare const createScoredStatement: (attemptStatement: UXapiStatement, result: UXapiStatement["result"]) => Pick<UXapiStatement, "object" | "verb" | "context" | "result">;
|
|
81
|
+
export declare const putScoredStatement: (gateway: LrsGateway, attemptStatement: UXapiStatement, result: UXapiStatement["result"]) => 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.putCompletedStatement = exports.createCompletedStatement = exports.putAttemptActivityStatement = exports.createAttemptActivityStatement = exports.putAttemptStatement = exports.createAttemptStatement = exports.createStatement = exports.loadActivityAttemptInfo = exports.loadStatementsForActivityAndFirstChildren = exports.resolveAttemptInfo = exports.mostRecentStatement = exports.oldestStatement = exports.resolveCompletedForAttempt = exports.resolveAttempts = exports.matchAttemptCompleted = exports.matchAttempt = void 0;
|
|
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.mostRecentStatement = exports.oldestStatement = exports.resolveScoredForAttempt = exports.resolveCompletedForAttempt = exports.resolveAttempts = exports.matchAttemptScored = exports.matchAttemptCompleted = exports.matchAttempt = exports.EXT_PENDING_SCORING = 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
|
|
@@ -21,7 +21,9 @@ var Verb;
|
|
|
21
21
|
(function (Verb) {
|
|
22
22
|
Verb["Attempted"] = "http://adlnet.gov/expapi/verbs/attempted";
|
|
23
23
|
Verb["Completed"] = "http://adlnet.gov/expapi/verbs/completed";
|
|
24
|
+
Verb["Scored"] = "http://adlnet.gov/expapi/verbs/scored";
|
|
24
25
|
})(Verb || (Verb = {}));
|
|
26
|
+
exports.EXT_PENDING_SCORING = 'https://openstax.org/xapi/extensions/pending-scoring';
|
|
25
27
|
const matchAttempt = (statement) => statement.verb.id === Verb.Attempted;
|
|
26
28
|
exports.matchAttempt = matchAttempt;
|
|
27
29
|
const matchAttemptCompleted = (attempt) => (statement) => {
|
|
@@ -32,6 +34,14 @@ const matchAttemptCompleted = (attempt) => (statement) => {
|
|
|
32
34
|
&& statement.context.registration === ((_b = attempt.context) === null || _b === void 0 ? void 0 : _b.registration);
|
|
33
35
|
};
|
|
34
36
|
exports.matchAttemptCompleted = matchAttemptCompleted;
|
|
37
|
+
const matchAttemptScored = (attempt) => (statement) => {
|
|
38
|
+
var _a, _b;
|
|
39
|
+
return statement.verb.id === Verb.Scored
|
|
40
|
+
&& statement.context !== undefined
|
|
41
|
+
&& ((_a = statement.context.statement) === null || _a === void 0 ? void 0 : _a.id) === attempt.id
|
|
42
|
+
&& statement.context.registration === ((_b = attempt.context) === null || _b === void 0 ? void 0 : _b.registration);
|
|
43
|
+
};
|
|
44
|
+
exports.matchAttemptScored = matchAttemptScored;
|
|
35
45
|
const resolveAttempts = (statements, options) => statements.filter(statement => {
|
|
36
46
|
var _a;
|
|
37
47
|
return (0, exports.matchAttempt)(statement)
|
|
@@ -42,6 +52,19 @@ exports.resolveAttempts = resolveAttempts;
|
|
|
42
52
|
const resolveCompletedForAttempt = (statements, attempt, activityIRI) => statements.find(statement => (0, exports.matchAttemptCompleted)(attempt)(statement)
|
|
43
53
|
&& (!activityIRI || statement.object.id === activityIRI));
|
|
44
54
|
exports.resolveCompletedForAttempt = resolveCompletedForAttempt;
|
|
55
|
+
const resolveScoredForAttempt = (statements, attempt, activityIRI) => {
|
|
56
|
+
// Prefer the most recent Scored
|
|
57
|
+
const scored = (0, exports.mostRecentStatement)(statements.filter(s => (0, exports.matchAttemptScored)(attempt)(s) && (!activityIRI || s.object.id === activityIRI)));
|
|
58
|
+
if (scored)
|
|
59
|
+
return scored;
|
|
60
|
+
// if no scored found, return most recent Completed with a score for the attempt.
|
|
61
|
+
const completedWithScore = (0, exports.mostRecentStatement)(statements.filter(s => (0, exports.matchAttemptCompleted)(attempt)(s) &&
|
|
62
|
+
(!activityIRI || s.object.id === activityIRI) && s.result &&
|
|
63
|
+
s.result.score &&
|
|
64
|
+
(s.result.score.scaled !== undefined)));
|
|
65
|
+
return completedWithScore;
|
|
66
|
+
};
|
|
67
|
+
exports.resolveScoredForAttempt = resolveScoredForAttempt;
|
|
45
68
|
const oldestStatement = (statements) => statements.reduce((result, statement) => result && (0, isBefore_1.default)((0, parseISO_1.default)('stored' in result && result.stored ? result.stored : result.timestamp), (0, parseISO_1.default)(statement.timestamp)) ? result : statement, statements[0]);
|
|
46
69
|
exports.oldestStatement = oldestStatement;
|
|
47
70
|
const mostRecentStatement = (statements) => statements.reduce((result, statement) => result && (0, isAfter_1.default)((0, parseISO_1.default)('stored' in result && result.stored ? result.stored : result.timestamp), (0, parseISO_1.default)(statement.timestamp)) ? result : statement, statements[0]);
|
|
@@ -57,55 +80,81 @@ const resolveAttemptInfo = (statements, options) => {
|
|
|
57
80
|
return acc;
|
|
58
81
|
}, []);
|
|
59
82
|
const completedPairs = resolveAttemptsWithCompleted(statements, attempts, options === null || options === void 0 ? void 0 : options.activityIRI);
|
|
60
|
-
const completedAttempts = completedPairs.
|
|
83
|
+
const completedAttempts = completedPairs.length;
|
|
61
84
|
/* the last attempt sorted by timestamp */
|
|
62
85
|
const currentAttempt = (options === null || options === void 0 ? void 0 : options.currentAttempt)
|
|
63
86
|
? attempts.find(attempt => attempt.id === options.currentAttempt)
|
|
64
87
|
: (options === null || options === void 0 ? void 0 : options.currentPreference) === 'oldest'
|
|
65
88
|
? (0, exports.oldestStatement)(attempts)
|
|
66
89
|
: (0, exports.mostRecentStatement)(attempts);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
90
|
+
const current = currentAttempt
|
|
91
|
+
? {
|
|
92
|
+
attempt: currentAttempt,
|
|
93
|
+
completed: (0, exports.resolveCompletedForAttempt)(statements, currentAttempt, options === null || options === void 0 ? void 0 : options.activityIRI),
|
|
94
|
+
scored: (0, exports.resolveScoredForAttempt)(statements, currentAttempt, options === null || options === void 0 ? void 0 : options.activityIRI),
|
|
95
|
+
}
|
|
96
|
+
: undefined;
|
|
97
|
+
const currentAttemptStatements = currentAttempt
|
|
98
|
+
? statements.filter(statement => {
|
|
99
|
+
var _a;
|
|
100
|
+
return (!(options === null || options === void 0 ? void 0 : options.activityIRI) || statement.object.id === options.activityIRI) &&
|
|
101
|
+
((_a = statement.context) === null || _a === void 0 ? void 0 : _a.registration) === currentAttempt.id;
|
|
102
|
+
})
|
|
103
|
+
: [];
|
|
74
104
|
const mostRecentPair = completedPairs.reduce((cur, pair) => {
|
|
75
105
|
if (!cur)
|
|
76
106
|
return pair;
|
|
77
107
|
return (0, isAfter_1.default)((0, parseISO_1.default)(cur.attempt.timestamp), (0, parseISO_1.default)(pair.attempt.timestamp)) ? cur : pair;
|
|
78
108
|
}, undefined);
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
109
|
+
const mostRecentWithCompleted = mostRecentPair
|
|
110
|
+
? {
|
|
111
|
+
attempt: mostRecentPair.attempt,
|
|
112
|
+
completed: (0, exports.resolveCompletedForAttempt)(statements, mostRecentPair.attempt, options === null || options === void 0 ? void 0 : options.activityIRI),
|
|
113
|
+
scored: (0, exports.resolveScoredForAttempt)(statements, mostRecentPair.attempt, options === null || options === void 0 ? void 0 : options.activityIRI),
|
|
114
|
+
}
|
|
115
|
+
: undefined;
|
|
116
|
+
const scaledForAttempt = (attempt) => {
|
|
86
117
|
var _a;
|
|
87
|
-
return
|
|
88
|
-
|
|
118
|
+
// resolveScoredForAttempt may return a Scored, or fallback to a Completed that has a score
|
|
119
|
+
const picked = (0, exports.resolveScoredForAttempt)(statements, attempt, options === null || options === void 0 ? void 0 : options.activityIRI);
|
|
120
|
+
const score = (_a = picked === null || picked === void 0 ? void 0 : picked.result) === null || _a === void 0 ? void 0 : _a.score;
|
|
121
|
+
if (!score)
|
|
122
|
+
return undefined;
|
|
123
|
+
// Prefer scaled, otherwise normalize raw/max if available
|
|
124
|
+
if (score.scaled !== undefined)
|
|
125
|
+
return score.scaled;
|
|
126
|
+
if (score.raw !== undefined && score.max !== undefined && score.max !== 0) {
|
|
127
|
+
return score.raw / score.max;
|
|
128
|
+
}
|
|
129
|
+
return undefined;
|
|
89
130
|
};
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (
|
|
94
|
-
return
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
131
|
+
let bestAttempt = completedPairs.reduce((best, { attempt }) => {
|
|
132
|
+
const bScaled = best !== undefined ? scaledForAttempt(best) : undefined;
|
|
133
|
+
const aScaled = scaledForAttempt(attempt);
|
|
134
|
+
if (bScaled === undefined)
|
|
135
|
+
return aScaled === undefined ? best : attempt;
|
|
136
|
+
if (aScaled === undefined)
|
|
137
|
+
return best;
|
|
138
|
+
return aScaled > bScaled ? attempt : best;
|
|
98
139
|
}, undefined);
|
|
140
|
+
// Fallback if no attempt had a score, use newest completed as best
|
|
141
|
+
if (!bestAttempt && mostRecentPair) {
|
|
142
|
+
bestAttempt = mostRecentPair.attempt;
|
|
143
|
+
}
|
|
144
|
+
const best = bestAttempt
|
|
145
|
+
? {
|
|
146
|
+
attempt: bestAttempt,
|
|
147
|
+
completed: (0, exports.resolveCompletedForAttempt)(statements, bestAttempt, options && options.activityIRI),
|
|
148
|
+
scored: (0, exports.resolveScoredForAttempt)(statements, bestAttempt, options && options.activityIRI),
|
|
149
|
+
}
|
|
150
|
+
: undefined;
|
|
99
151
|
return {
|
|
100
152
|
attempts: attempts.length,
|
|
101
|
-
|
|
102
|
-
bestCompletedAttemptCompleted: bestPair === null || bestPair === void 0 ? void 0 : bestPair.completed,
|
|
103
|
-
completedAttempts: completedAttempts.length,
|
|
104
|
-
currentAttempt,
|
|
105
|
-
currentAttemptCompleted,
|
|
153
|
+
completedAttempts,
|
|
106
154
|
currentAttemptStatements,
|
|
107
|
-
|
|
108
|
-
|
|
155
|
+
current,
|
|
156
|
+
best,
|
|
157
|
+
mostRecentWithCompleted,
|
|
109
158
|
};
|
|
110
159
|
};
|
|
111
160
|
exports.resolveAttemptInfo = resolveAttemptInfo;
|
|
@@ -281,3 +330,46 @@ const putCompletedStatement = async (gateway, attemptStatement, result) => {
|
|
|
281
330
|
return (await gateway.putXapiStatements([(0, exports.createCompletedStatement)(attemptStatement, result)]))[0];
|
|
282
331
|
};
|
|
283
332
|
exports.putCompletedStatement = putCompletedStatement;
|
|
333
|
+
// pending score statement for when the open written response has been graded.
|
|
334
|
+
const createCompletedPendingScoringStatement = (attemptStatement) => (0, exports.createCompletedStatement)(attemptStatement, {
|
|
335
|
+
extensions: { [exports.EXT_PENDING_SCORING]: 'true' },
|
|
336
|
+
});
|
|
337
|
+
exports.createCompletedPendingScoringStatement = createCompletedPendingScoringStatement;
|
|
338
|
+
const putCompletedPendingScoringStatement = async (gateway, attemptStatement) => {
|
|
339
|
+
return (await gateway.putXapiStatements([
|
|
340
|
+
(0, exports.createCompletedPendingScoringStatement)(attemptStatement),
|
|
341
|
+
]))[0];
|
|
342
|
+
};
|
|
343
|
+
exports.putCompletedPendingScoringStatement = putCompletedPendingScoringStatement;
|
|
344
|
+
// scored statement for when the open written response has been graded.
|
|
345
|
+
const createScoredStatement = (attemptStatement, result) => {
|
|
346
|
+
var _a, _b;
|
|
347
|
+
return {
|
|
348
|
+
context: {
|
|
349
|
+
...(((_a = attemptStatement.context) === null || _a === void 0 ? void 0 : _a.contextActivities) ? {
|
|
350
|
+
contextActivities: attemptStatement.context.contextActivities,
|
|
351
|
+
} : {}),
|
|
352
|
+
...(((_b = attemptStatement.context) === null || _b === void 0 ? void 0 : _b.registration) ? {
|
|
353
|
+
registration: attemptStatement.context.registration,
|
|
354
|
+
} : {}),
|
|
355
|
+
statement: {
|
|
356
|
+
objectType: 'StatementRef',
|
|
357
|
+
id: attemptStatement.id,
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
object: attemptStatement.object,
|
|
361
|
+
verb: {
|
|
362
|
+
display: { 'en-US': 'Scored' },
|
|
363
|
+
id: Verb.Scored,
|
|
364
|
+
},
|
|
365
|
+
result: {
|
|
366
|
+
// no duration here, we treat Scored as a pure grading event
|
|
367
|
+
...result,
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
};
|
|
371
|
+
exports.createScoredStatement = createScoredStatement;
|
|
372
|
+
const putScoredStatement = async (gateway, attemptStatement, result) => {
|
|
373
|
+
return (await gateway.putXapiStatements([(0, exports.createScoredStatement)(attemptStatement, result)]))[0];
|
|
374
|
+
};
|
|
375
|
+
exports.putScoredStatement = putScoredStatement;
|
|
@@ -59,31 +59,32 @@ const getScoreGrade = (score, options) => {
|
|
|
59
59
|
};
|
|
60
60
|
exports.getScoreGrade = getScoreGrade;
|
|
61
61
|
// These methods assign 0's to incomplete activities
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
attempt: state.bestCompletedAttempt,
|
|
66
|
-
completed: state.bestCompletedAttemptCompleted,
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
// return current attempt if gradePreference is current
|
|
62
|
+
const pickAttemptAndCompletedOrScored = (state, gradePreference) => {
|
|
63
|
+
var _a;
|
|
64
|
+
const entry = (gradePreference === 'best' ? state.best : state.current);
|
|
70
65
|
return {
|
|
71
|
-
attempt:
|
|
72
|
-
|
|
66
|
+
attempt: entry === null || entry === void 0 ? void 0 : entry.attempt,
|
|
67
|
+
completedOrScored: (_a = entry === null || entry === void 0 ? void 0 : entry.scored) !== null && _a !== void 0 ? _a : entry === null || entry === void 0 ? void 0 : entry.completed,
|
|
68
|
+
completed: entry === null || entry === void 0 ? void 0 : entry.completed,
|
|
73
69
|
};
|
|
74
70
|
};
|
|
75
71
|
const getCompletedActivityStateGradeAndProgress = ({ name, scoreMaximum, state, userId, gradePreference = 'current' }) => {
|
|
76
|
-
var _a;
|
|
77
|
-
const { attempt, completed } =
|
|
72
|
+
var _a, _b, _c;
|
|
73
|
+
const { attempt, completedOrScored, completed } = pickAttemptAndCompletedOrScored(state, gradePreference);
|
|
74
|
+
const grade = (0, exports.getScoreGrade)(((_a = completedOrScored === null || completedOrScored === void 0 ? void 0 : completedOrScored.result) === null || _a === void 0 ? void 0 : _a.score) || {}, {
|
|
75
|
+
maxScore: scoreMaximum,
|
|
76
|
+
startedAt: attempt === null || attempt === void 0 ? void 0 : attempt.timestamp,
|
|
77
|
+
submittedAt: completed === null || completed === void 0 ? void 0 : completed.timestamp,
|
|
78
|
+
userId,
|
|
79
|
+
});
|
|
80
|
+
// If this Completed is explicitly "pending scoring", surface that in the grade.
|
|
81
|
+
if (((_c = (_b = completedOrScored === null || completedOrScored === void 0 ? void 0 : completedOrScored.result) === null || _b === void 0 ? void 0 : _b.extensions) === null || _c === void 0 ? void 0 : _c[attempt_utils_1.EXT_PENDING_SCORING]) === 'true') {
|
|
82
|
+
grade.gradingProgress = 'PendingManual';
|
|
83
|
+
}
|
|
78
84
|
return {
|
|
79
|
-
grade
|
|
80
|
-
maxScore: scoreMaximum,
|
|
81
|
-
startedAt: attempt === null || attempt === void 0 ? void 0 : attempt.timestamp,
|
|
82
|
-
submittedAt: completed === null || completed === void 0 ? void 0 : completed.timestamp,
|
|
83
|
-
userId,
|
|
84
|
-
}),
|
|
85
|
+
grade,
|
|
85
86
|
progress: {
|
|
86
|
-
scaled:
|
|
87
|
+
scaled: completedOrScored ? 1 : 0,
|
|
87
88
|
},
|
|
88
89
|
name,
|
|
89
90
|
};
|
|
@@ -115,7 +116,7 @@ const getCurrentGrade = async (services, registration, options) => {
|
|
|
115
116
|
gradePreference,
|
|
116
117
|
});
|
|
117
118
|
}
|
|
118
|
-
if (userInfo.
|
|
119
|
+
if (userInfo.current && userInfo.current.completed || !incompleteAttemptCallback) {
|
|
119
120
|
return getCompletedActivityStateGradeAndProgress({
|
|
120
121
|
name,
|
|
121
122
|
scoreMaximum,
|
|
@@ -137,8 +138,8 @@ const getAssignmentGrades = async (services, assignmentIRI, registration, option
|
|
|
137
138
|
}
|
|
138
139
|
// Partition based on whether the chosen preference has a completed attempt
|
|
139
140
|
const [incompleteInfo, completedInfo] = (0, partition_1.default)((info) => {
|
|
140
|
-
const {
|
|
141
|
-
return
|
|
141
|
+
const { completedOrScored } = pickAttemptAndCompletedOrScored(info.data, gradePreference);
|
|
142
|
+
return completedOrScored === undefined;
|
|
142
143
|
})(mappedInfo);
|
|
143
144
|
return (0, exports.getCompletedUserInfosGradeAndProgress)(completedInfo, scoreMaximum, gradePreference).concat(await incompleteAttemptsCallback(incompleteInfo));
|
|
144
145
|
};
|