@openstax/ts-utils 1.33.1 → 1.34.1
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/fileServer/s3FileServer.js +1 -2
- package/dist/cjs/services/lrsGateway/attempt-utils.d.ts +2 -0
- package/dist/cjs/services/lrsGateway/attempt-utils.js +32 -7
- package/dist/cjs/services/lrsGateway/xapiUtils.d.ts +4 -1
- package/dist/cjs/services/lrsGateway/xapiUtils.js +54 -18
- package/dist/cjs/tsconfig.without-specs.cjs.tsbuildinfo +1 -1
- package/dist/esm/services/fileServer/s3FileServer.js +1 -2
- package/dist/esm/services/lrsGateway/attempt-utils.d.ts +2 -0
- package/dist/esm/services/lrsGateway/attempt-utils.js +32 -7
- package/dist/esm/services/lrsGateway/xapiUtils.d.ts +4 -1
- package/dist/esm/services/lrsGateway/xapiUtils.js +52 -17
- 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
|
@@ -68,8 +68,7 @@ const s3FileServer = (initializer) => (configProvider) => {
|
|
|
68
68
|
const Conditions = [
|
|
69
69
|
{ acl: 'private' },
|
|
70
70
|
{ bucket },
|
|
71
|
-
['starts-with', '$key', prefix]
|
|
72
|
-
['starts-with', '$Content-Type', ''],
|
|
71
|
+
['starts-with', '$key', prefix]
|
|
73
72
|
];
|
|
74
73
|
const defaultFields = {
|
|
75
74
|
acl: 'private',
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { LrsGateway, UXapiStatement } from '.';
|
|
2
2
|
export type ActivityState = {
|
|
3
3
|
attempts: number;
|
|
4
|
+
bestCompletedAttempt?: UXapiStatement;
|
|
5
|
+
bestCompletedAttemptCompleted?: UXapiStatement;
|
|
4
6
|
completedAttempts: number;
|
|
5
7
|
currentAttempt?: UXapiStatement;
|
|
6
8
|
currentAttemptCompleted?: UXapiStatement;
|
|
@@ -50,7 +50,14 @@ const resolveAttemptInfo = (statements, options) => {
|
|
|
50
50
|
// TODO optimize. i'm 100% that this could all be done in one iteration but i'm not messing around with that for now.
|
|
51
51
|
const attempts = (0, exports.resolveAttempts)(statements, options);
|
|
52
52
|
/* attempts that have a completed statement */
|
|
53
|
-
const
|
|
53
|
+
const resolveAttemptsWithCompleted = (statements, attempts, activityIRI) => attempts.reduce((acc, attempt) => {
|
|
54
|
+
const completed = (0, exports.resolveCompletedForAttempt)(statements, attempt, activityIRI);
|
|
55
|
+
if (completed)
|
|
56
|
+
acc.push({ attempt, completed });
|
|
57
|
+
return acc;
|
|
58
|
+
}, []);
|
|
59
|
+
const completedPairs = resolveAttemptsWithCompleted(statements, attempts, options === null || options === void 0 ? void 0 : options.activityIRI);
|
|
60
|
+
const completedAttempts = completedPairs.map(p => p.attempt);
|
|
54
61
|
/* the last attempt sorted by timestamp */
|
|
55
62
|
const currentAttempt = (options === null || options === void 0 ? void 0 : options.currentAttempt)
|
|
56
63
|
? attempts.find(attempt => attempt.id === options.currentAttempt)
|
|
@@ -64,23 +71,41 @@ const resolveAttemptInfo = (statements, options) => {
|
|
|
64
71
|
&& ((_a = statement.context) === null || _a === void 0 ? void 0 : _a.registration) === currentAttempt.id;
|
|
65
72
|
}) : [];
|
|
66
73
|
const currentAttemptCompleted = currentAttempt && (0, exports.resolveCompletedForAttempt)(statements, currentAttempt, options === null || options === void 0 ? void 0 : options.activityIRI);
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
74
|
+
const mostRecentPair = completedPairs.reduce((cur, pair) => {
|
|
75
|
+
if (!cur)
|
|
76
|
+
return pair;
|
|
77
|
+
return (0, isAfter_1.default)((0, parseISO_1.default)(cur.attempt.timestamp), (0, parseISO_1.default)(pair.attempt.timestamp)) ? cur : pair;
|
|
78
|
+
}, undefined);
|
|
70
79
|
/*
|
|
71
80
|
* the structure allows for the possibility of multiple incomplete attempts.
|
|
72
81
|
* the implementation can choose at its discretion to ignore the currentAttempt
|
|
73
82
|
* and instead make a new one, for instance if the implementation desires
|
|
74
83
|
* an attempt timeout feature
|
|
75
84
|
*/
|
|
85
|
+
const hasScaledScore = (p) => {
|
|
86
|
+
var _a;
|
|
87
|
+
return p.completed.result !== undefined &&
|
|
88
|
+
typeof ((_a = p.completed.result.score) === null || _a === void 0 ? void 0 : _a.scaled) === 'number';
|
|
89
|
+
};
|
|
90
|
+
// Filter to only scored pairs with scaled.
|
|
91
|
+
const scoredPairs = completedPairs.filter(hasScaledScore);
|
|
92
|
+
const bestPair = scoredPairs.reduce((best, pair) => {
|
|
93
|
+
if (!best)
|
|
94
|
+
return pair;
|
|
95
|
+
return pair.completed.result.score.scaled > best.completed.result.score.scaled
|
|
96
|
+
? pair
|
|
97
|
+
: best;
|
|
98
|
+
}, undefined);
|
|
76
99
|
return {
|
|
77
100
|
attempts: attempts.length,
|
|
101
|
+
bestCompletedAttempt: bestPair === null || bestPair === void 0 ? void 0 : bestPair.attempt,
|
|
102
|
+
bestCompletedAttemptCompleted: bestPair === null || bestPair === void 0 ? void 0 : bestPair.completed,
|
|
78
103
|
completedAttempts: completedAttempts.length,
|
|
79
104
|
currentAttempt,
|
|
80
|
-
currentAttemptCompleted
|
|
105
|
+
currentAttemptCompleted,
|
|
81
106
|
currentAttemptStatements,
|
|
82
|
-
mostRecentAttemptWithCompleted,
|
|
83
|
-
mostRecentAttemptWithCompletedCompleted,
|
|
107
|
+
mostRecentAttemptWithCompleted: mostRecentPair === null || mostRecentPair === void 0 ? void 0 : mostRecentPair.attempt,
|
|
108
|
+
mostRecentAttemptWithCompletedCompleted: mostRecentPair === null || mostRecentPair === void 0 ? void 0 : mostRecentPair.completed,
|
|
84
109
|
};
|
|
85
110
|
};
|
|
86
111
|
exports.resolveAttemptInfo = resolveAttemptInfo;
|
|
@@ -44,6 +44,8 @@ export type GradeAndProgress = {
|
|
|
44
44
|
progress: Progress;
|
|
45
45
|
name?: string;
|
|
46
46
|
};
|
|
47
|
+
export type UserActivityInfo = MappedUserInfo<ActivityState>;
|
|
48
|
+
export declare const getCompletedUserInfosGradeAndProgress: (infos: UserActivityInfo[], scoreMaximum?: number, gradePreference?: "current" | "best") => GradeAndProgress[];
|
|
47
49
|
export declare const getCurrentGrade: (services: {
|
|
48
50
|
lrs: LrsGateway;
|
|
49
51
|
ltiAuthProvider: AuthProvider;
|
|
@@ -53,8 +55,8 @@ export declare const getCurrentGrade: (services: {
|
|
|
53
55
|
name?: string;
|
|
54
56
|
scoreMaximum?: number;
|
|
55
57
|
userId?: string;
|
|
58
|
+
gradePreference?: "current" | "best";
|
|
56
59
|
}) => Promise<GradeAndProgress | null>;
|
|
57
|
-
export type UserActivityInfo = MappedUserInfo<ActivityState>;
|
|
58
60
|
export declare const getAssignmentGrades: (services: {
|
|
59
61
|
accountsGateway: AccountsGateway;
|
|
60
62
|
lrs: LrsGateway;
|
|
@@ -65,4 +67,5 @@ export declare const getAssignmentGrades: (services: {
|
|
|
65
67
|
platformId?: string;
|
|
66
68
|
scoreMaximum?: number;
|
|
67
69
|
user?: string;
|
|
70
|
+
gradePreference?: "current" | "best";
|
|
68
71
|
}) => Promise<GradeAndProgress[]>;
|
|
@@ -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.getAssignmentGrades = exports.getCurrentGrade = exports.getScoreGrade = exports.getRegistrationAttemptInfo = void 0;
|
|
6
|
+
exports.getAssignmentGrades = exports.getCurrentGrade = exports.getCompletedUserInfosGradeAndProgress = exports.getScoreGrade = exports.getRegistrationAttemptInfo = void 0;
|
|
7
7
|
const partition_1 = __importDefault(require("lodash/fp/partition"));
|
|
8
8
|
const __1 = require("../..");
|
|
9
9
|
const attempt_utils_1 = require("./attempt-utils");
|
|
@@ -59,24 +59,43 @@ 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
|
-
|
|
62
|
+
const pickAttemptAndCompleted = (state, gradePreference) => {
|
|
63
|
+
if (gradePreference === 'best' && state.bestCompletedAttempt) {
|
|
64
|
+
return {
|
|
65
|
+
attempt: state.bestCompletedAttempt,
|
|
66
|
+
completed: state.bestCompletedAttemptCompleted,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
// return current attempt if gradePreference is current
|
|
70
|
+
return {
|
|
71
|
+
attempt: state.currentAttempt,
|
|
72
|
+
completed: state.currentAttemptCompleted,
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
const getCompletedActivityStateGradeAndProgress = ({ name, scoreMaximum, state, userId, gradePreference = 'current' }) => {
|
|
76
|
+
var _a;
|
|
77
|
+
const { attempt, completed } = pickAttemptAndCompleted(state, gradePreference);
|
|
78
|
+
return {
|
|
79
|
+
grade: (0, exports.getScoreGrade)(((_a = completed === null || completed === void 0 ? void 0 : completed.result) === null || _a === void 0 ? void 0 : _a.score) || {}, {
|
|
66
80
|
maxScore: scoreMaximum,
|
|
67
|
-
startedAt:
|
|
68
|
-
submittedAt:
|
|
81
|
+
startedAt: attempt === null || attempt === void 0 ? void 0 : attempt.timestamp,
|
|
82
|
+
submittedAt: completed === null || completed === void 0 ? void 0 : completed.timestamp,
|
|
69
83
|
userId,
|
|
70
84
|
}),
|
|
71
85
|
progress: {
|
|
72
|
-
scaled:
|
|
86
|
+
scaled: completed ? 1 : 0,
|
|
73
87
|
},
|
|
74
88
|
name,
|
|
75
|
-
}
|
|
89
|
+
};
|
|
76
90
|
};
|
|
77
|
-
const getCompletedUserInfosGradeAndProgress = (infos, scoreMaximum) => infos.map(({ data, fullName, platformUserId }) => getCompletedActivityStateGradeAndProgress({
|
|
78
|
-
name: fullName,
|
|
91
|
+
const getCompletedUserInfosGradeAndProgress = (infos, scoreMaximum, gradePreference) => infos.map(({ data, fullName, platformUserId }) => getCompletedActivityStateGradeAndProgress({
|
|
92
|
+
name: fullName,
|
|
93
|
+
scoreMaximum,
|
|
94
|
+
userId: platformUserId,
|
|
95
|
+
state: data,
|
|
96
|
+
gradePreference,
|
|
79
97
|
}));
|
|
98
|
+
exports.getCompletedUserInfosGradeAndProgress = getCompletedUserInfosGradeAndProgress;
|
|
80
99
|
const getCurrentGrade = async (services, registration, options) => {
|
|
81
100
|
var _a;
|
|
82
101
|
const user = await services.ltiAuthProvider.getUser();
|
|
@@ -84,26 +103,43 @@ const getCurrentGrade = async (services, registration, options) => {
|
|
|
84
103
|
return null;
|
|
85
104
|
}
|
|
86
105
|
const userId = (_a = options === null || options === void 0 ? void 0 : options.userId) !== null && _a !== void 0 ? _a : user.uuid;
|
|
87
|
-
const { currentPreference, incompleteAttemptCallback, name, scoreMaximum } = options !== null && options !== void 0 ? options : {};
|
|
106
|
+
const { currentPreference, incompleteAttemptCallback, name, scoreMaximum, gradePreference, } = options !== null && options !== void 0 ? options : {};
|
|
88
107
|
const infoPerUser = await (0, exports.getRegistrationAttemptInfo)(services.lrs, registration, { currentPreference });
|
|
89
108
|
const userInfo = infoPerUser[user.uuid];
|
|
90
109
|
if (!userInfo) {
|
|
91
|
-
return getCompletedActivityStateGradeAndProgress({
|
|
110
|
+
return getCompletedActivityStateGradeAndProgress({
|
|
111
|
+
name,
|
|
112
|
+
scoreMaximum,
|
|
113
|
+
state: (0, attempt_utils_1.resolveAttemptInfo)([]),
|
|
114
|
+
userId,
|
|
115
|
+
gradePreference,
|
|
116
|
+
});
|
|
92
117
|
}
|
|
93
118
|
if (userInfo.currentAttemptCompleted || !incompleteAttemptCallback) {
|
|
94
|
-
return getCompletedActivityStateGradeAndProgress({
|
|
119
|
+
return getCompletedActivityStateGradeAndProgress({
|
|
120
|
+
name,
|
|
121
|
+
scoreMaximum,
|
|
122
|
+
state: userInfo,
|
|
123
|
+
userId,
|
|
124
|
+
gradePreference,
|
|
125
|
+
});
|
|
95
126
|
}
|
|
96
127
|
return incompleteAttemptCallback(userInfo);
|
|
97
128
|
};
|
|
98
129
|
exports.getCurrentGrade = getCurrentGrade;
|
|
99
130
|
const getAssignmentGrades = async (services, assignmentIRI, registration, options) => {
|
|
100
|
-
const { anyUser, currentPreference, incompleteAttemptsCallback, platformId, scoreMaximum, user } = options !== null && options !== void 0 ? options : {};
|
|
131
|
+
const { anyUser, currentPreference, incompleteAttemptsCallback, platformId, scoreMaximum, user, gradePreference = 'current', } = options !== null && options !== void 0 ? options : {};
|
|
101
132
|
const infoPerUserUuid = await (0, exports.getRegistrationAttemptInfo)(services.lrs, registration, { activity: assignmentIRI, anyUser, currentPreference, user });
|
|
102
133
|
const mappedInfo = await services.accountsGateway.mapUserUuids(infoPerUserUuid, platformId);
|
|
134
|
+
// If no custom callback, just return graded results based on preference
|
|
103
135
|
if (!incompleteAttemptsCallback) {
|
|
104
|
-
return getCompletedUserInfosGradeAndProgress(mappedInfo, scoreMaximum);
|
|
136
|
+
return (0, exports.getCompletedUserInfosGradeAndProgress)(mappedInfo, scoreMaximum, gradePreference);
|
|
105
137
|
}
|
|
106
|
-
|
|
107
|
-
|
|
138
|
+
// Partition based on whether the chosen preference has a completed attempt
|
|
139
|
+
const [incompleteInfo, completedInfo] = (0, partition_1.default)((info) => {
|
|
140
|
+
const { completed } = pickAttemptAndCompleted(info.data, gradePreference);
|
|
141
|
+
return completed === undefined;
|
|
142
|
+
})(mappedInfo);
|
|
143
|
+
return (0, exports.getCompletedUserInfosGradeAndProgress)(completedInfo, scoreMaximum, gradePreference).concat(await incompleteAttemptsCallback(incompleteInfo));
|
|
108
144
|
};
|
|
109
145
|
exports.getAssignmentGrades = getAssignmentGrades;
|