@openstax/ts-utils 1.38.0-beta → 1.38.0
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 +9 -4
- package/dist/cjs/services/lrsGateway/attempt-utils.js +74 -89
- package/dist/cjs/services/lrsGateway/xapiUtils.d.ts +2 -9
- package/dist/cjs/services/lrsGateway/xapiUtils.js +21 -34
- package/dist/cjs/tsconfig.without-specs.cjs.tsbuildinfo +1 -1
- package/dist/esm/services/lrsGateway/attempt-utils.d.ts +9 -4
- package/dist/esm/services/lrsGateway/attempt-utils.js +71 -86
- package/dist/esm/services/lrsGateway/xapiUtils.d.ts +2 -9
- package/dist/esm/services/lrsGateway/xapiUtils.js +22 -35
- package/dist/esm/tsconfig.without-specs.esm.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -14,16 +14,21 @@ export type ActivityState = {
|
|
|
14
14
|
mostRecentWithCompleted?: AttemptEntry;
|
|
15
15
|
};
|
|
16
16
|
export declare const matchAttempt: (statement: UXapiStatement) => boolean;
|
|
17
|
-
export declare const matchAttemptCompleted: (attempt: UXapiStatement) => (statement: UXapiStatement) => boolean;
|
|
18
|
-
export declare const matchAttemptScored: (attempt: UXapiStatement) => (statement: UXapiStatement) => boolean;
|
|
17
|
+
export declare const matchAttemptCompleted: (attempt: UXapiStatement) => (statement: UXapiStatement) => boolean | "";
|
|
18
|
+
export declare const matchAttemptScored: (attempt: UXapiStatement) => (statement: UXapiStatement) => boolean | "";
|
|
19
19
|
export declare const resolveAttempts: (statements: UXapiStatement[], options?: {
|
|
20
20
|
activityIRI?: string;
|
|
21
21
|
parentActivityAttempt?: string;
|
|
22
22
|
}) => UXapiStatement[];
|
|
23
23
|
export declare const resolveCompletedForAttempt: (statements: UXapiStatement[], attempt: UXapiStatement, activityIRI?: string) => UXapiStatement | undefined;
|
|
24
24
|
export declare const resolveScoredForAttempt: (statements: UXapiStatement[], attempt: UXapiStatement, activityIRI?: string) => UXapiStatement | undefined;
|
|
25
|
-
export declare const
|
|
26
|
-
export declare const
|
|
25
|
+
export declare const getStatementTimeString: (statement: UXapiStatement) => string;
|
|
26
|
+
export declare const getStatementTime: (statement: UXapiStatement) => Date;
|
|
27
|
+
export declare const oldestStatement: (items: UXapiStatement[]) => UXapiStatement | undefined;
|
|
28
|
+
export declare const mostRecentStatement: (items: UXapiStatement[]) => UXapiStatement | undefined;
|
|
29
|
+
export declare const oldestEntry: (items: AttemptEntry[]) => AttemptEntry | undefined;
|
|
30
|
+
export declare const mostRecentEntry: (items: AttemptEntry[]) => AttemptEntry | undefined;
|
|
31
|
+
export declare const bestEntry: (items: AttemptEntry[]) => AttemptEntry | undefined;
|
|
27
32
|
export declare const resolveAttemptInfo: (statements: UXapiStatement[], options?: {
|
|
28
33
|
activityIRI?: string;
|
|
29
34
|
currentAttempt?: string;
|
|
@@ -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.mostRecentStatement = exports.oldestStatement = exports.resolveScoredForAttempt = exports.resolveCompletedForAttempt = exports.resolveAttempts = exports.matchAttemptScored = exports.matchAttemptCompleted = exports.matchAttempt = exports.EXT_PENDING_SCORING = 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.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;
|
|
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
|
|
@@ -28,7 +28,7 @@ const matchAttempt = (statement) => statement.verb.id === Verb.Attempted;
|
|
|
28
28
|
exports.matchAttempt = matchAttempt;
|
|
29
29
|
const matchAttemptCompleted = (attempt) => (statement) => {
|
|
30
30
|
var _a, _b;
|
|
31
|
-
return statement.verb.id === Verb.Completed
|
|
31
|
+
return statement.verb.id === Verb.Completed && attempt.id
|
|
32
32
|
&& statement.context !== undefined
|
|
33
33
|
&& ((_a = statement.context.statement) === null || _a === void 0 ? void 0 : _a.id) === attempt.id
|
|
34
34
|
&& statement.context.registration === ((_b = attempt.context) === null || _b === void 0 ? void 0 : _b.registration);
|
|
@@ -36,7 +36,7 @@ const matchAttemptCompleted = (attempt) => (statement) => {
|
|
|
36
36
|
exports.matchAttemptCompleted = matchAttemptCompleted;
|
|
37
37
|
const matchAttemptScored = (attempt) => (statement) => {
|
|
38
38
|
var _a, _b;
|
|
39
|
-
return statement.verb.id === Verb.Scored
|
|
39
|
+
return statement.verb.id === Verb.Scored && attempt.id
|
|
40
40
|
&& statement.context !== undefined
|
|
41
41
|
&& ((_a = statement.context.statement) === null || _a === void 0 ? void 0 : _a.id) === attempt.id
|
|
42
42
|
&& statement.context.registration === ((_b = attempt.context) === null || _b === void 0 ? void 0 : _b.registration);
|
|
@@ -53,104 +53,89 @@ const resolveCompletedForAttempt = (statements, attempt, activityIRI) => stateme
|
|
|
53
53
|
&& (!activityIRI || statement.object.id === activityIRI));
|
|
54
54
|
exports.resolveCompletedForAttempt = resolveCompletedForAttempt;
|
|
55
55
|
const resolveScoredForAttempt = (statements, attempt, activityIRI) => {
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
return completedWithScore;
|
|
56
|
+
// scored or completed statements that contain a score in their
|
|
57
|
+
// result are 'scoring statements' for our purposes.
|
|
58
|
+
const scoringStatements = statements.filter(s => {
|
|
59
|
+
var _a;
|
|
60
|
+
return ((0, exports.matchAttemptScored)(attempt)(s) || (0, exports.matchAttemptCompleted)(attempt)(s))
|
|
61
|
+
&& ((_a = s.result) === null || _a === void 0 ? void 0 : _a.score)
|
|
62
|
+
&& (!activityIRI || s.object.id === activityIRI);
|
|
63
|
+
});
|
|
64
|
+
return (0, exports.mostRecentStatement)(scoringStatements);
|
|
66
65
|
};
|
|
67
66
|
exports.resolveScoredForAttempt = resolveScoredForAttempt;
|
|
68
|
-
const
|
|
69
|
-
exports.
|
|
70
|
-
const
|
|
71
|
-
|
|
67
|
+
const resolveAttemptEntries = (statements, attempts, activityIRI) => attempts.reduce((acc, attempt) => {
|
|
68
|
+
const completed = (0, exports.resolveCompletedForAttempt)(statements, attempt, activityIRI);
|
|
69
|
+
const scored = (0, exports.resolveScoredForAttempt)(statements, attempt, activityIRI);
|
|
70
|
+
acc.push({ attempt, completed, scored });
|
|
71
|
+
return acc;
|
|
72
|
+
}, []);
|
|
73
|
+
const getStatementTimeString = (statement) => 'stored' in statement && statement.stored ? statement.stored : statement.timestamp;
|
|
74
|
+
exports.getStatementTimeString = getStatementTimeString;
|
|
75
|
+
const getStatementTime = (statement) => (0, parseISO_1.default)((0, exports.getStatementTimeString)(statement));
|
|
76
|
+
exports.getStatementTime = getStatementTime;
|
|
77
|
+
const findByComparator = (comparator, extractor) => (items) => {
|
|
78
|
+
return items.reduce((best, item) => {
|
|
79
|
+
if (!best)
|
|
80
|
+
return item;
|
|
81
|
+
return comparator(extractor(best), extractor(item)) ? best : item;
|
|
82
|
+
}, undefined);
|
|
83
|
+
};
|
|
84
|
+
exports.oldestStatement = findByComparator(isBefore_1.default, exports.getStatementTime);
|
|
85
|
+
exports.mostRecentStatement = findByComparator(isAfter_1.default, exports.getStatementTime);
|
|
86
|
+
exports.oldestEntry = findByComparator(isBefore_1.default, (entry) => (0, exports.getStatementTime)(entry.attempt));
|
|
87
|
+
exports.mostRecentEntry = findByComparator(isAfter_1.default, (entry) => (0, exports.getStatementTime)(entry.attempt));
|
|
88
|
+
const scaledScoreForEntry = (entry) => {
|
|
89
|
+
var _a;
|
|
90
|
+
const scoringStatement = entry.scored;
|
|
91
|
+
const score = (_a = scoringStatement === null || scoringStatement === void 0 ? void 0 : scoringStatement.result) === null || _a === void 0 ? void 0 : _a.score;
|
|
92
|
+
if (!score)
|
|
93
|
+
return { score: undefined, entry };
|
|
94
|
+
// Prefer scaled, otherwise normalize raw/max if available
|
|
95
|
+
if (score.scaled !== undefined)
|
|
96
|
+
return { score: score.scaled, entry };
|
|
97
|
+
if (score.raw !== undefined && score.max !== undefined && score.max !== 0) {
|
|
98
|
+
return { score: score.raw / score.max, entry };
|
|
99
|
+
}
|
|
100
|
+
return { score: undefined, entry };
|
|
101
|
+
};
|
|
102
|
+
/*
|
|
103
|
+
* the 'best' entry is the one with the highest scaled score.
|
|
104
|
+
* if two entries have no score, the most recent is considered best.
|
|
105
|
+
*/
|
|
106
|
+
exports.bestEntry = findByComparator((a, b) => {
|
|
107
|
+
// if neither has a score, use the most recent
|
|
108
|
+
if (a.score === undefined && b.score === undefined) {
|
|
109
|
+
return (0, isAfter_1.default)((0, exports.getStatementTime)(a.entry.attempt), (0, exports.getStatementTime)(b.entry.attempt));
|
|
110
|
+
}
|
|
111
|
+
if (a.score === undefined)
|
|
112
|
+
return false;
|
|
113
|
+
if (b.score === undefined)
|
|
114
|
+
return true;
|
|
115
|
+
return a.score > b.score;
|
|
116
|
+
}, scaledScoreForEntry);
|
|
72
117
|
const resolveAttemptInfo = (statements, options) => {
|
|
73
118
|
// TODO optimize. i'm 100% that this could all be done in one iteration but i'm not messing around with that for now.
|
|
74
119
|
const attempts = (0, exports.resolveAttempts)(statements, options);
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
const completedAttempts = completedPairs.length;
|
|
84
|
-
/* the last attempt sorted by timestamp */
|
|
85
|
-
const currentAttempt = (options === null || options === void 0 ? void 0 : options.currentAttempt)
|
|
86
|
-
? attempts.find(attempt => attempt.id === options.currentAttempt)
|
|
87
|
-
: (options === null || options === void 0 ? void 0 : options.currentPreference) === 'oldest'
|
|
88
|
-
? (0, exports.oldestStatement)(attempts)
|
|
89
|
-
: (0, exports.mostRecentStatement)(attempts);
|
|
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
|
|
120
|
+
const attemptEntries = resolveAttemptEntries(statements, attempts, options === null || options === void 0 ? void 0 : options.activityIRI);
|
|
121
|
+
const completedEntries = attemptEntries.filter((entry) => !!entry.completed);
|
|
122
|
+
const current = (options === null || options === void 0 ? void 0 : options.currentAttempt)
|
|
123
|
+
? attemptEntries.find(({ attempt }) => attempt.id === options.currentAttempt)
|
|
124
|
+
: (options === null || options === void 0 ? void 0 : options.currentPreference) === 'oldest' /* the attempt sorted by timestamp */
|
|
125
|
+
? (0, exports.oldestEntry)(attemptEntries)
|
|
126
|
+
: (0, exports.mostRecentEntry)(attemptEntries);
|
|
127
|
+
const currentAttemptStatements = current
|
|
98
128
|
? statements.filter(statement => {
|
|
99
129
|
var _a;
|
|
100
130
|
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) ===
|
|
131
|
+
((_a = statement.context) === null || _a === void 0 ? void 0 : _a.registration) === current.attempt.id;
|
|
102
132
|
})
|
|
103
133
|
: [];
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
return pair;
|
|
107
|
-
return (0, isAfter_1.default)((0, parseISO_1.default)(cur.attempt.timestamp), (0, parseISO_1.default)(pair.attempt.timestamp)) ? cur : pair;
|
|
108
|
-
}, undefined);
|
|
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) => {
|
|
117
|
-
var _a;
|
|
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;
|
|
130
|
-
};
|
|
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;
|
|
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;
|
|
134
|
+
const mostRecentWithCompleted = (0, exports.mostRecentEntry)(completedEntries);
|
|
135
|
+
const best = (0, exports.bestEntry)(completedEntries);
|
|
151
136
|
return {
|
|
152
137
|
attempts: attempts.length,
|
|
153
|
-
completedAttempts,
|
|
138
|
+
completedAttempts: completedEntries.length,
|
|
154
139
|
currentAttemptStatements,
|
|
155
140
|
current,
|
|
156
141
|
best,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AccountsGateway, MappedUserInfo } from '../accountsGateway';
|
|
2
2
|
import { AuthProvider } from '../authProvider';
|
|
3
|
-
import { ActivityState } from './attempt-utils';
|
|
3
|
+
import { ActivityState, AttemptEntry } from './attempt-utils';
|
|
4
4
|
import { LrsGateway } from '.';
|
|
5
5
|
export interface Grade {
|
|
6
6
|
activityProgress: 'Initialized' | 'Started' | 'inProgress' | 'Submitted' | 'Completed';
|
|
@@ -23,15 +23,8 @@ export declare const getRegistrationAttemptInfo: (lrs: LrsGateway, registration:
|
|
|
23
23
|
}) => Promise<{
|
|
24
24
|
[key: string]: ActivityState;
|
|
25
25
|
}>;
|
|
26
|
-
export declare const getScoreGrade: (
|
|
27
|
-
scaled?: number;
|
|
28
|
-
raw?: number;
|
|
29
|
-
min?: number;
|
|
30
|
-
max?: number;
|
|
31
|
-
}, options: {
|
|
26
|
+
export declare const getScoreGrade: (entry: Partial<AttemptEntry>, options: {
|
|
32
27
|
maxScore?: number;
|
|
33
|
-
startedAt?: string;
|
|
34
|
-
submittedAt?: string;
|
|
35
28
|
userId?: string;
|
|
36
29
|
}) => Grade;
|
|
37
30
|
export type Progress = {
|
|
@@ -30,28 +30,28 @@ exports.getRegistrationAttemptInfo = getRegistrationAttemptInfo;
|
|
|
30
30
|
// lti: http://www.imsglobal.org/spec/lti-ags/v2p0#score-publish-service
|
|
31
31
|
// ltijs: https://cvmcosta.me/ltijs/#/grading
|
|
32
32
|
// Note: "min" is currently completely ignored
|
|
33
|
-
const getScoreGrade = (
|
|
34
|
-
|
|
35
|
-
const {
|
|
33
|
+
const getScoreGrade = (entry, options) => {
|
|
34
|
+
var _a, _b, _c, _d, _e;
|
|
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
|
+
const { maxScore, userId } = options;
|
|
36
37
|
const scoreMaximum = maxScore !== null && maxScore !== void 0 ? maxScore : 100;
|
|
37
38
|
const scoreGiven = raw && max
|
|
38
39
|
? scoreMaximum / max * raw
|
|
39
40
|
: scaled
|
|
40
41
|
? scaled * scoreMaximum
|
|
41
42
|
: 0;
|
|
42
|
-
const submission = {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
43
|
+
const submission = {
|
|
44
|
+
startedAt: entry.attempt ? (0, attempt_utils_1.getStatementTimeString)(entry.attempt) : undefined,
|
|
45
|
+
submittedAt: entry.completed ? (0, attempt_utils_1.getStatementTimeString)(entry.completed) : undefined,
|
|
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';
|
|
49
49
|
return {
|
|
50
50
|
userId,
|
|
51
|
-
activityProgress:
|
|
51
|
+
activityProgress: entry.completed ? 'Completed' : 'Started',
|
|
52
52
|
// canvas assumes that anything that isn't 'FullyGraded' requires manual grading and displays a "needs grading" icon.
|
|
53
53
|
// if you warp your mind you can consider the portion of the assignment which is completed to be fully graded.
|
|
54
|
-
gradingProgress: 'FullyGraded',
|
|
54
|
+
gradingProgress: pendingManual ? 'PendingManual' : 'FullyGraded',
|
|
55
55
|
scoreMaximum,
|
|
56
56
|
scoreGiven: (0, __1.roundToPrecision)(scoreGiven, -2),
|
|
57
57
|
submission,
|
|
@@ -59,32 +59,19 @@ 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
|
-
const entry = (gradePreference === 'best' ? state.best : state.current);
|
|
65
|
-
return {
|
|
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,
|
|
69
|
-
};
|
|
62
|
+
const getGradingAttemptEntry = (state, gradePreference) => {
|
|
63
|
+
return (gradePreference === 'best' ? state.best : state.current) || {};
|
|
70
64
|
};
|
|
71
65
|
const getCompletedActivityStateGradeAndProgress = ({ name, scoreMaximum, state, userId, gradePreference = 'current' }) => {
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
const grade = (0, exports.getScoreGrade)(((_a = completedOrScored === null || completedOrScored === void 0 ? void 0 : completedOrScored.result) === null || _a === void 0 ? void 0 : _a.score) || {}, {
|
|
66
|
+
const entry = getGradingAttemptEntry(state, gradePreference);
|
|
67
|
+
const grade = (0, exports.getScoreGrade)(entry, {
|
|
75
68
|
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
69
|
userId,
|
|
79
70
|
});
|
|
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
|
-
}
|
|
84
71
|
return {
|
|
85
72
|
grade,
|
|
86
73
|
progress: {
|
|
87
|
-
scaled:
|
|
74
|
+
scaled: entry.completed ? 1 : 0,
|
|
88
75
|
},
|
|
89
76
|
name,
|
|
90
77
|
};
|
|
@@ -98,7 +85,7 @@ const getCompletedUserInfosGradeAndProgress = (infos, scoreMaximum, gradePrefere
|
|
|
98
85
|
}));
|
|
99
86
|
exports.getCompletedUserInfosGradeAndProgress = getCompletedUserInfosGradeAndProgress;
|
|
100
87
|
const getCurrentGrade = async (services, registration, options) => {
|
|
101
|
-
var _a;
|
|
88
|
+
var _a, _b;
|
|
102
89
|
const user = await services.ltiAuthProvider.getUser();
|
|
103
90
|
if (!user) {
|
|
104
91
|
return null;
|
|
@@ -116,7 +103,7 @@ const getCurrentGrade = async (services, registration, options) => {
|
|
|
116
103
|
gradePreference,
|
|
117
104
|
});
|
|
118
105
|
}
|
|
119
|
-
if (userInfo.current
|
|
106
|
+
if (((_b = userInfo.current) === null || _b === void 0 ? void 0 : _b.completed) || !incompleteAttemptCallback) {
|
|
120
107
|
return getCompletedActivityStateGradeAndProgress({
|
|
121
108
|
name,
|
|
122
109
|
scoreMaximum,
|
|
@@ -138,8 +125,8 @@ const getAssignmentGrades = async (services, assignmentIRI, registration, option
|
|
|
138
125
|
}
|
|
139
126
|
// Partition based on whether the chosen preference has a completed attempt
|
|
140
127
|
const [incompleteInfo, completedInfo] = (0, partition_1.default)((info) => {
|
|
141
|
-
const {
|
|
142
|
-
return
|
|
128
|
+
const { completed } = getGradingAttemptEntry(info.data, gradePreference);
|
|
129
|
+
return completed === undefined;
|
|
143
130
|
})(mappedInfo);
|
|
144
131
|
return (0, exports.getCompletedUserInfosGradeAndProgress)(completedInfo, scoreMaximum, gradePreference).concat(await incompleteAttemptsCallback(incompleteInfo));
|
|
145
132
|
};
|