@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
|
@@ -62,8 +62,7 @@ export const s3FileServer = (initializer) => (configProvider) => {
|
|
|
62
62
|
const Conditions = [
|
|
63
63
|
{ acl: 'private' },
|
|
64
64
|
{ bucket },
|
|
65
|
-
['starts-with', '$key', prefix]
|
|
66
|
-
['starts-with', '$Content-Type', ''],
|
|
65
|
+
['starts-with', '$key', prefix]
|
|
67
66
|
];
|
|
68
67
|
const defaultFields = {
|
|
69
68
|
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;
|
|
@@ -38,7 +38,14 @@ export const resolveAttemptInfo = (statements, options) => {
|
|
|
38
38
|
// TODO optimize. i'm 100% that this could all be done in one iteration but i'm not messing around with that for now.
|
|
39
39
|
const attempts = resolveAttempts(statements, options);
|
|
40
40
|
/* attempts that have a completed statement */
|
|
41
|
-
const
|
|
41
|
+
const resolveAttemptsWithCompleted = (statements, attempts, activityIRI) => attempts.reduce((acc, attempt) => {
|
|
42
|
+
const completed = resolveCompletedForAttempt(statements, attempt, activityIRI);
|
|
43
|
+
if (completed)
|
|
44
|
+
acc.push({ attempt, completed });
|
|
45
|
+
return acc;
|
|
46
|
+
}, []);
|
|
47
|
+
const completedPairs = resolveAttemptsWithCompleted(statements, attempts, options === null || options === void 0 ? void 0 : options.activityIRI);
|
|
48
|
+
const completedAttempts = completedPairs.map(p => p.attempt);
|
|
42
49
|
/* the last attempt sorted by timestamp */
|
|
43
50
|
const currentAttempt = (options === null || options === void 0 ? void 0 : options.currentAttempt)
|
|
44
51
|
? attempts.find(attempt => attempt.id === options.currentAttempt)
|
|
@@ -52,23 +59,41 @@ export const resolveAttemptInfo = (statements, options) => {
|
|
|
52
59
|
&& ((_a = statement.context) === null || _a === void 0 ? void 0 : _a.registration) === currentAttempt.id;
|
|
53
60
|
}) : [];
|
|
54
61
|
const currentAttemptCompleted = currentAttempt && resolveCompletedForAttempt(statements, currentAttempt, options === null || options === void 0 ? void 0 : options.activityIRI);
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
62
|
+
const mostRecentPair = completedPairs.reduce((cur, pair) => {
|
|
63
|
+
if (!cur)
|
|
64
|
+
return pair;
|
|
65
|
+
return isAfter(parseISO(cur.attempt.timestamp), parseISO(pair.attempt.timestamp)) ? cur : pair;
|
|
66
|
+
}, undefined);
|
|
58
67
|
/*
|
|
59
68
|
* the structure allows for the possibility of multiple incomplete attempts.
|
|
60
69
|
* the implementation can choose at its discretion to ignore the currentAttempt
|
|
61
70
|
* and instead make a new one, for instance if the implementation desires
|
|
62
71
|
* an attempt timeout feature
|
|
63
72
|
*/
|
|
73
|
+
const hasScaledScore = (p) => {
|
|
74
|
+
var _a;
|
|
75
|
+
return p.completed.result !== undefined &&
|
|
76
|
+
typeof ((_a = p.completed.result.score) === null || _a === void 0 ? void 0 : _a.scaled) === 'number';
|
|
77
|
+
};
|
|
78
|
+
// Filter to only scored pairs with scaled.
|
|
79
|
+
const scoredPairs = completedPairs.filter(hasScaledScore);
|
|
80
|
+
const bestPair = scoredPairs.reduce((best, pair) => {
|
|
81
|
+
if (!best)
|
|
82
|
+
return pair;
|
|
83
|
+
return pair.completed.result.score.scaled > best.completed.result.score.scaled
|
|
84
|
+
? pair
|
|
85
|
+
: best;
|
|
86
|
+
}, undefined);
|
|
64
87
|
return {
|
|
65
88
|
attempts: attempts.length,
|
|
89
|
+
bestCompletedAttempt: bestPair === null || bestPair === void 0 ? void 0 : bestPair.attempt,
|
|
90
|
+
bestCompletedAttemptCompleted: bestPair === null || bestPair === void 0 ? void 0 : bestPair.completed,
|
|
66
91
|
completedAttempts: completedAttempts.length,
|
|
67
92
|
currentAttempt,
|
|
68
|
-
currentAttemptCompleted
|
|
93
|
+
currentAttemptCompleted,
|
|
69
94
|
currentAttemptStatements,
|
|
70
|
-
mostRecentAttemptWithCompleted,
|
|
71
|
-
mostRecentAttemptWithCompletedCompleted,
|
|
95
|
+
mostRecentAttemptWithCompleted: mostRecentPair === null || mostRecentPair === void 0 ? void 0 : mostRecentPair.attempt,
|
|
96
|
+
mostRecentAttemptWithCompletedCompleted: mostRecentPair === null || mostRecentPair === void 0 ? void 0 : mostRecentPair.completed,
|
|
72
97
|
};
|
|
73
98
|
};
|
|
74
99
|
/*
|
|
@@ -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[]>;
|
|
@@ -51,23 +51,41 @@ export const getScoreGrade = (score, options) => {
|
|
|
51
51
|
};
|
|
52
52
|
};
|
|
53
53
|
// These methods assign 0's to incomplete activities
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
const pickAttemptAndCompleted = (state, gradePreference) => {
|
|
55
|
+
if (gradePreference === 'best' && state.bestCompletedAttempt) {
|
|
56
|
+
return {
|
|
57
|
+
attempt: state.bestCompletedAttempt,
|
|
58
|
+
completed: state.bestCompletedAttemptCompleted,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
// return current attempt if gradePreference is current
|
|
62
|
+
return {
|
|
63
|
+
attempt: state.currentAttempt,
|
|
64
|
+
completed: state.currentAttemptCompleted,
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
const getCompletedActivityStateGradeAndProgress = ({ name, scoreMaximum, state, userId, gradePreference = 'current' }) => {
|
|
68
|
+
var _a;
|
|
69
|
+
const { attempt, completed } = pickAttemptAndCompleted(state, gradePreference);
|
|
70
|
+
return {
|
|
71
|
+
grade: getScoreGrade(((_a = completed === null || completed === void 0 ? void 0 : completed.result) === null || _a === void 0 ? void 0 : _a.score) || {}, {
|
|
58
72
|
maxScore: scoreMaximum,
|
|
59
|
-
startedAt:
|
|
60
|
-
submittedAt:
|
|
73
|
+
startedAt: attempt === null || attempt === void 0 ? void 0 : attempt.timestamp,
|
|
74
|
+
submittedAt: completed === null || completed === void 0 ? void 0 : completed.timestamp,
|
|
61
75
|
userId,
|
|
62
76
|
}),
|
|
63
77
|
progress: {
|
|
64
|
-
scaled:
|
|
78
|
+
scaled: completed ? 1 : 0,
|
|
65
79
|
},
|
|
66
80
|
name,
|
|
67
|
-
}
|
|
81
|
+
};
|
|
68
82
|
};
|
|
69
|
-
const getCompletedUserInfosGradeAndProgress = (infos, scoreMaximum) => infos.map(({ data, fullName, platformUserId }) => getCompletedActivityStateGradeAndProgress({
|
|
70
|
-
name: fullName,
|
|
83
|
+
export const getCompletedUserInfosGradeAndProgress = (infos, scoreMaximum, gradePreference) => infos.map(({ data, fullName, platformUserId }) => getCompletedActivityStateGradeAndProgress({
|
|
84
|
+
name: fullName,
|
|
85
|
+
scoreMaximum,
|
|
86
|
+
userId: platformUserId,
|
|
87
|
+
state: data,
|
|
88
|
+
gradePreference,
|
|
71
89
|
}));
|
|
72
90
|
export const getCurrentGrade = async (services, registration, options) => {
|
|
73
91
|
var _a;
|
|
@@ -76,24 +94,41 @@ export const getCurrentGrade = async (services, registration, options) => {
|
|
|
76
94
|
return null;
|
|
77
95
|
}
|
|
78
96
|
const userId = (_a = options === null || options === void 0 ? void 0 : options.userId) !== null && _a !== void 0 ? _a : user.uuid;
|
|
79
|
-
const { currentPreference, incompleteAttemptCallback, name, scoreMaximum } = options !== null && options !== void 0 ? options : {};
|
|
97
|
+
const { currentPreference, incompleteAttemptCallback, name, scoreMaximum, gradePreference, } = options !== null && options !== void 0 ? options : {};
|
|
80
98
|
const infoPerUser = await getRegistrationAttemptInfo(services.lrs, registration, { currentPreference });
|
|
81
99
|
const userInfo = infoPerUser[user.uuid];
|
|
82
100
|
if (!userInfo) {
|
|
83
|
-
return getCompletedActivityStateGradeAndProgress({
|
|
101
|
+
return getCompletedActivityStateGradeAndProgress({
|
|
102
|
+
name,
|
|
103
|
+
scoreMaximum,
|
|
104
|
+
state: resolveAttemptInfo([]),
|
|
105
|
+
userId,
|
|
106
|
+
gradePreference,
|
|
107
|
+
});
|
|
84
108
|
}
|
|
85
109
|
if (userInfo.currentAttemptCompleted || !incompleteAttemptCallback) {
|
|
86
|
-
return getCompletedActivityStateGradeAndProgress({
|
|
110
|
+
return getCompletedActivityStateGradeAndProgress({
|
|
111
|
+
name,
|
|
112
|
+
scoreMaximum,
|
|
113
|
+
state: userInfo,
|
|
114
|
+
userId,
|
|
115
|
+
gradePreference,
|
|
116
|
+
});
|
|
87
117
|
}
|
|
88
118
|
return incompleteAttemptCallback(userInfo);
|
|
89
119
|
};
|
|
90
120
|
export const getAssignmentGrades = async (services, assignmentIRI, registration, options) => {
|
|
91
|
-
const { anyUser, currentPreference, incompleteAttemptsCallback, platformId, scoreMaximum, user } = options !== null && options !== void 0 ? options : {};
|
|
121
|
+
const { anyUser, currentPreference, incompleteAttemptsCallback, platformId, scoreMaximum, user, gradePreference = 'current', } = options !== null && options !== void 0 ? options : {};
|
|
92
122
|
const infoPerUserUuid = await getRegistrationAttemptInfo(services.lrs, registration, { activity: assignmentIRI, anyUser, currentPreference, user });
|
|
93
123
|
const mappedInfo = await services.accountsGateway.mapUserUuids(infoPerUserUuid, platformId);
|
|
124
|
+
// If no custom callback, just return graded results based on preference
|
|
94
125
|
if (!incompleteAttemptsCallback) {
|
|
95
|
-
return getCompletedUserInfosGradeAndProgress(mappedInfo, scoreMaximum);
|
|
126
|
+
return getCompletedUserInfosGradeAndProgress(mappedInfo, scoreMaximum, gradePreference);
|
|
96
127
|
}
|
|
97
|
-
|
|
98
|
-
|
|
128
|
+
// Partition based on whether the chosen preference has a completed attempt
|
|
129
|
+
const [incompleteInfo, completedInfo] = partition((info) => {
|
|
130
|
+
const { completed } = pickAttemptAndCompleted(info.data, gradePreference);
|
|
131
|
+
return completed === undefined;
|
|
132
|
+
})(mappedInfo);
|
|
133
|
+
return getCompletedUserInfosGradeAndProgress(completedInfo, scoreMaximum, gradePreference).concat(await incompleteAttemptsCallback(incompleteInfo));
|
|
99
134
|
};
|