@playcademy/sandbox 0.3.17-beta.22 → 0.3.17-beta.24
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/cli.js +84 -19
- package/dist/server.js +84 -19
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1330,7 +1330,7 @@ var package_default;
|
|
|
1330
1330
|
var init_package = __esm(() => {
|
|
1331
1331
|
package_default = {
|
|
1332
1332
|
name: "@playcademy/sandbox",
|
|
1333
|
-
version: "0.3.17-beta.
|
|
1333
|
+
version: "0.3.17-beta.24",
|
|
1334
1334
|
description: "Local development server for Playcademy game development",
|
|
1335
1335
|
type: "module",
|
|
1336
1336
|
exports: {
|
|
@@ -30893,13 +30893,54 @@ class TimebackAdminService {
|
|
|
30893
30893
|
const remediationItems = events.map((event) => mapCaliperEventToRemediationActivity(event, relevantCourseIds)).filter((item) => Boolean(item));
|
|
30894
30894
|
return [...groupedGameplayItems, ...remediationItems].toSorted((a, b) => b.occurredAt.localeCompare(a.occurredAt));
|
|
30895
30895
|
}
|
|
30896
|
-
async getStudentEnrollmentsByCourseId(client, studentId, courseIds) {
|
|
30897
|
-
const
|
|
30898
|
-
const
|
|
30899
|
-
|
|
30896
|
+
async getStudentEnrollmentsByCourseId(client, studentId, courseIds, options) {
|
|
30897
|
+
const enrollments = new Map;
|
|
30898
|
+
const entries = await Promise.all(courseIds.map(async (courseId) => {
|
|
30899
|
+
const roster = await client.oneroster.enrollments.listByCourse(courseId, {
|
|
30900
|
+
includeInactive: options?.includeInactive,
|
|
30901
|
+
includeUsers: false
|
|
30902
|
+
});
|
|
30903
|
+
const matches = roster.filter((entry) => entry.enrollment.user.sourcedId === studentId).toSorted((a, b) => {
|
|
30904
|
+
const aActive = a.enrollment.status === "active";
|
|
30905
|
+
const bActive = b.enrollment.status === "active";
|
|
30906
|
+
if (aActive !== bActive) {
|
|
30907
|
+
return aActive ? -1 : 1;
|
|
30908
|
+
}
|
|
30909
|
+
return (b.enrollment.dateLastModified ?? "").localeCompare(a.enrollment.dateLastModified ?? "");
|
|
30910
|
+
});
|
|
30911
|
+
return { courseId, match: matches[0] ?? null };
|
|
30912
|
+
}));
|
|
30913
|
+
for (const { courseId, match } of entries) {
|
|
30914
|
+
if (match) {
|
|
30915
|
+
enrollments.set(courseId, {
|
|
30916
|
+
id: match.enrollment.sourcedId,
|
|
30917
|
+
status: match.enrollment.status ?? "active",
|
|
30918
|
+
role: match.enrollment.role ?? "student",
|
|
30919
|
+
beginDate: match.enrollment.beginDate ?? null,
|
|
30920
|
+
endDate: match.enrollment.endDate ?? null,
|
|
30921
|
+
course: {
|
|
30922
|
+
id: courseId,
|
|
30923
|
+
title: match.class?.title ?? "",
|
|
30924
|
+
subjects: null,
|
|
30925
|
+
grades: null
|
|
30926
|
+
}
|
|
30927
|
+
});
|
|
30928
|
+
}
|
|
30929
|
+
}
|
|
30930
|
+
return { enrollments };
|
|
30900
30931
|
}
|
|
30901
30932
|
async assertStudentEnrolledInCourse(client, studentId, courseId) {
|
|
30902
|
-
const
|
|
30933
|
+
const enrollments = await client.edubridge.enrollments.listByUser(studentId);
|
|
30934
|
+
const match = enrollments.find((e) => e.course.id === courseId);
|
|
30935
|
+
if (!match) {
|
|
30936
|
+
throw new NotFoundError("Student enrollment", `${studentId}:${courseId}`);
|
|
30937
|
+
}
|
|
30938
|
+
return match;
|
|
30939
|
+
}
|
|
30940
|
+
async assertStudentHasEnrollmentInCourse(client, studentId, courseId) {
|
|
30941
|
+
const { enrollments: enrollmentsByCourseId } = await this.getStudentEnrollmentsByCourseId(client, studentId, [courseId], {
|
|
30942
|
+
includeInactive: true
|
|
30943
|
+
});
|
|
30903
30944
|
if (!enrollmentsByCourseId.has(courseId)) {
|
|
30904
30945
|
throw new NotFoundError("Student enrollment", `${studentId}:${courseId}`);
|
|
30905
30946
|
}
|
|
@@ -30952,7 +30993,7 @@ class TimebackAdminService {
|
|
|
30952
30993
|
return [];
|
|
30953
30994
|
}
|
|
30954
30995
|
}
|
|
30955
|
-
async listStudentsForCourse(gameId, courseId, user) {
|
|
30996
|
+
async listStudentsForCourse(gameId, courseId, user, options) {
|
|
30956
30997
|
const client = this.requireClient();
|
|
30957
30998
|
await this.deps.validateGameManagementAccess(user, gameId);
|
|
30958
30999
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
@@ -30962,7 +31003,8 @@ class TimebackAdminService {
|
|
|
30962
31003
|
throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
|
|
30963
31004
|
}
|
|
30964
31005
|
const roster = await client.oneroster.enrollments.listByCourse(courseId, {
|
|
30965
|
-
role: "student"
|
|
31006
|
+
role: "student",
|
|
31007
|
+
includeInactive: options?.includeInactive
|
|
30966
31008
|
});
|
|
30967
31009
|
const analyticsByEnrollmentId = await this.loadEnrollmentAnalyticsSummaries(roster.map((rosterEntry) => rosterEntry.enrollment.sourcedId).filter((enrollmentId) => Boolean(enrollmentId)));
|
|
30968
31010
|
const masterableUnits = await this.getMasterableUnits(courseId);
|
|
@@ -30971,6 +31013,7 @@ class TimebackAdminService {
|
|
|
30971
31013
|
const summary = enrollmentId ? analyticsByEnrollmentId.get(enrollmentId) : undefined;
|
|
30972
31014
|
const analyticsUnavailable = Boolean(enrollmentId) && summary?.analyticsAvailable !== true;
|
|
30973
31015
|
const name3 = rosterEntry.user ? `${rosterEntry.user.givenName} ${rosterEntry.user.familyName}`.trim() : rosterEntry.enrollment.user.sourcedId;
|
|
31016
|
+
const inactive = rosterEntry.enrollment.status === "tobedeleted";
|
|
30974
31017
|
return {
|
|
30975
31018
|
studentId: rosterEntry.enrollment.user.sourcedId,
|
|
30976
31019
|
enrollmentId,
|
|
@@ -30984,11 +31027,25 @@ class TimebackAdminService {
|
|
|
30984
31027
|
activeTimeSeconds: summary?.activeTimeSeconds ?? 0,
|
|
30985
31028
|
masteredUnits: summary?.masteredUnits ?? 0,
|
|
30986
31029
|
masterableUnits,
|
|
30987
|
-
pctCompleteApp: TimebackAdminService.computeCompletionPct(summary?.masteredUnits ?? 0, masterableUnits)
|
|
31030
|
+
pctCompleteApp: TimebackAdminService.computeCompletionPct(summary?.masteredUnits ?? 0, masterableUnits),
|
|
31031
|
+
...inactive ? { inactive } : {}
|
|
30988
31032
|
};
|
|
30989
31033
|
});
|
|
30990
|
-
|
|
30991
|
-
|
|
31034
|
+
const studentMap = new Map;
|
|
31035
|
+
for (const student of students) {
|
|
31036
|
+
const existing = studentMap.get(student.studentId);
|
|
31037
|
+
if (!existing || existing.inactive && !student.inactive) {
|
|
31038
|
+
studentMap.set(student.studentId, student);
|
|
31039
|
+
}
|
|
31040
|
+
}
|
|
31041
|
+
const deduped = [...studentMap.values()];
|
|
31042
|
+
deduped.sort((a, b) => {
|
|
31043
|
+
if (a.inactive !== b.inactive) {
|
|
31044
|
+
return a.inactive ? 1 : -1;
|
|
31045
|
+
}
|
|
31046
|
+
return a.name.localeCompare(b.name);
|
|
31047
|
+
});
|
|
31048
|
+
return { gameId, courseId, students: deduped };
|
|
30992
31049
|
}
|
|
30993
31050
|
async getStudentOverview(gameId, studentId, user, courseId) {
|
|
30994
31051
|
const client = this.requireClient();
|
|
@@ -31000,7 +31057,9 @@ class TimebackAdminService {
|
|
|
31000
31057
|
throw new NotFoundError("Timeback integration", gameId);
|
|
31001
31058
|
}
|
|
31002
31059
|
const courseIds = new Set(integrations.map((integration) => integration.courseId));
|
|
31003
|
-
const enrollmentsByCourseId = await this.getStudentEnrollmentsByCourseId(client, studentId, [...courseIds]
|
|
31060
|
+
const { enrollments: enrollmentsByCourseId } = await this.getStudentEnrollmentsByCourseId(client, studentId, [...courseIds], {
|
|
31061
|
+
includeInactive: true
|
|
31062
|
+
});
|
|
31004
31063
|
if (enrollmentsByCourseId.size === 0) {
|
|
31005
31064
|
throw new NotFoundError("Student enrollment", courseId ? `${studentId}:${courseId}` : `${studentId}:${gameId}`);
|
|
31006
31065
|
}
|
|
@@ -31015,6 +31074,7 @@ class TimebackAdminService {
|
|
|
31015
31074
|
const summary = enrollment ? analyticsByEnrollmentId.get(enrollment.id) : undefined;
|
|
31016
31075
|
const masterableUnits = masterableUnitsByCourse.get(integration.courseId);
|
|
31017
31076
|
const analyticsUnavailable = Boolean(enrollment?.id) && summary?.analyticsAvailable !== true;
|
|
31077
|
+
const inactive = enrollment?.status === "tobedeleted";
|
|
31018
31078
|
return {
|
|
31019
31079
|
courseId: integration.courseId,
|
|
31020
31080
|
title: enrollment?.course.title || `${integration.subject} Grade ${integration.grade}`,
|
|
@@ -31029,7 +31089,8 @@ class TimebackAdminService {
|
|
|
31029
31089
|
masterableUnits,
|
|
31030
31090
|
pctCompleteApp: TimebackAdminService.computeCompletionPct(summary?.masteredUnits ?? 0, masterableUnits),
|
|
31031
31091
|
completionStatus: completionStatusByCourse.get(integration.courseId) ?? "none",
|
|
31032
|
-
history: summary?.history ?? []
|
|
31092
|
+
history: summary?.history ?? [],
|
|
31093
|
+
...inactive ? { inactive } : {}
|
|
31033
31094
|
};
|
|
31034
31095
|
});
|
|
31035
31096
|
return {
|
|
@@ -31056,7 +31117,7 @@ class TimebackAdminService {
|
|
|
31056
31117
|
if (!integration) {
|
|
31057
31118
|
throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
|
|
31058
31119
|
}
|
|
31059
|
-
await this.
|
|
31120
|
+
await this.assertStudentHasEnrollmentInCourse(client, studentId, courseId);
|
|
31060
31121
|
const relevantCourseIds = new Set([courseId]);
|
|
31061
31122
|
const fetchLimit = Math.min(safeOffset + safeLimit + 1, TimebackAdminService.MAX_STUDENT_ACTIVITY_OFFSET + TimebackAdminService.MAX_STUDENT_ACTIVITY_LIMIT + 1);
|
|
31062
31123
|
const allActivities = await this.listRecentActivityForStudent(client, studentId, gameSource, relevantCourseIds, fetchLimit);
|
|
@@ -31077,7 +31138,7 @@ class TimebackAdminService {
|
|
|
31077
31138
|
if (!integration) {
|
|
31078
31139
|
throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
|
|
31079
31140
|
}
|
|
31080
|
-
await this.
|
|
31141
|
+
await this.assertStudentHasEnrollmentInCourse(client, studentId, courseId);
|
|
31081
31142
|
const events = await this.fetchCaliperEventsForStudent(client, studentId, gameSource, TimebackAdminService.MAX_RECENT_ACTIVITY_EVENT_FETCH);
|
|
31082
31143
|
const relevantCourseIds = new Set([courseId]);
|
|
31083
31144
|
let matchedEvents;
|
|
@@ -35684,7 +35745,7 @@ function createOneRosterNamespace(client) {
|
|
|
35684
35745
|
listByClass: async (classSourcedId, options) => {
|
|
35685
35746
|
const queryParams = new URLSearchParams;
|
|
35686
35747
|
const filters = [`class.sourcedId='${escapeFilterValue2(classSourcedId)}'`];
|
|
35687
|
-
if (options?.
|
|
35748
|
+
if (!options?.includeInactive) {
|
|
35688
35749
|
filters.push(`status='active'`);
|
|
35689
35750
|
}
|
|
35690
35751
|
queryParams.set("filter", filters.join(" AND "));
|
|
@@ -35719,7 +35780,7 @@ function createOneRosterNamespace(client) {
|
|
|
35719
35780
|
const queryParams = new URLSearchParams;
|
|
35720
35781
|
const classFilter = batch.map((classId) => `class.sourcedId='${escapeFilterValue2(classId)}'`).join(" OR ");
|
|
35721
35782
|
const filters = [batch.length > 1 ? `(${classFilter})` : classFilter];
|
|
35722
|
-
if (options?.
|
|
35783
|
+
if (!options?.includeInactive) {
|
|
35723
35784
|
filters.push(`status='active'`);
|
|
35724
35785
|
}
|
|
35725
35786
|
if (options?.role) {
|
|
@@ -96846,15 +96907,19 @@ var init_timeback_controller = __esm(() => {
|
|
|
96846
96907
|
getRoster = requireGameManagementAccess(async (ctx) => {
|
|
96847
96908
|
const gameId = ctx.params.gameId;
|
|
96848
96909
|
const courseId = ctx.params.courseId;
|
|
96910
|
+
const includeInactive = ctx.url.searchParams.get("includeInactive") === "true";
|
|
96849
96911
|
if (!gameId || !courseId) {
|
|
96850
96912
|
throw ApiError.badRequest("Missing gameId or courseId parameter");
|
|
96851
96913
|
}
|
|
96852
96914
|
logger64.debug("Getting course roster", {
|
|
96853
96915
|
requesterId: ctx.user.id,
|
|
96854
96916
|
gameId,
|
|
96855
|
-
courseId
|
|
96917
|
+
courseId,
|
|
96918
|
+
includeInactive
|
|
96919
|
+
});
|
|
96920
|
+
return ctx.services.timebackAdmin.listStudentsForCourse(gameId, courseId, ctx.user, {
|
|
96921
|
+
includeInactive
|
|
96856
96922
|
});
|
|
96857
|
-
return ctx.services.timebackAdmin.listStudentsForCourse(gameId, courseId, ctx.user);
|
|
96858
96923
|
});
|
|
96859
96924
|
getStudentOverview = requireGameManagementAccess(async (ctx) => {
|
|
96860
96925
|
const timebackId = ctx.params.timebackId;
|
package/dist/server.js
CHANGED
|
@@ -1329,7 +1329,7 @@ var package_default;
|
|
|
1329
1329
|
var init_package = __esm(() => {
|
|
1330
1330
|
package_default = {
|
|
1331
1331
|
name: "@playcademy/sandbox",
|
|
1332
|
-
version: "0.3.17-beta.
|
|
1332
|
+
version: "0.3.17-beta.24",
|
|
1333
1333
|
description: "Local development server for Playcademy game development",
|
|
1334
1334
|
type: "module",
|
|
1335
1335
|
exports: {
|
|
@@ -30892,13 +30892,54 @@ class TimebackAdminService {
|
|
|
30892
30892
|
const remediationItems = events.map((event) => mapCaliperEventToRemediationActivity(event, relevantCourseIds)).filter((item) => Boolean(item));
|
|
30893
30893
|
return [...groupedGameplayItems, ...remediationItems].toSorted((a, b) => b.occurredAt.localeCompare(a.occurredAt));
|
|
30894
30894
|
}
|
|
30895
|
-
async getStudentEnrollmentsByCourseId(client, studentId, courseIds) {
|
|
30896
|
-
const
|
|
30897
|
-
const
|
|
30898
|
-
|
|
30895
|
+
async getStudentEnrollmentsByCourseId(client, studentId, courseIds, options) {
|
|
30896
|
+
const enrollments = new Map;
|
|
30897
|
+
const entries = await Promise.all(courseIds.map(async (courseId) => {
|
|
30898
|
+
const roster = await client.oneroster.enrollments.listByCourse(courseId, {
|
|
30899
|
+
includeInactive: options?.includeInactive,
|
|
30900
|
+
includeUsers: false
|
|
30901
|
+
});
|
|
30902
|
+
const matches = roster.filter((entry) => entry.enrollment.user.sourcedId === studentId).toSorted((a, b) => {
|
|
30903
|
+
const aActive = a.enrollment.status === "active";
|
|
30904
|
+
const bActive = b.enrollment.status === "active";
|
|
30905
|
+
if (aActive !== bActive) {
|
|
30906
|
+
return aActive ? -1 : 1;
|
|
30907
|
+
}
|
|
30908
|
+
return (b.enrollment.dateLastModified ?? "").localeCompare(a.enrollment.dateLastModified ?? "");
|
|
30909
|
+
});
|
|
30910
|
+
return { courseId, match: matches[0] ?? null };
|
|
30911
|
+
}));
|
|
30912
|
+
for (const { courseId, match } of entries) {
|
|
30913
|
+
if (match) {
|
|
30914
|
+
enrollments.set(courseId, {
|
|
30915
|
+
id: match.enrollment.sourcedId,
|
|
30916
|
+
status: match.enrollment.status ?? "active",
|
|
30917
|
+
role: match.enrollment.role ?? "student",
|
|
30918
|
+
beginDate: match.enrollment.beginDate ?? null,
|
|
30919
|
+
endDate: match.enrollment.endDate ?? null,
|
|
30920
|
+
course: {
|
|
30921
|
+
id: courseId,
|
|
30922
|
+
title: match.class?.title ?? "",
|
|
30923
|
+
subjects: null,
|
|
30924
|
+
grades: null
|
|
30925
|
+
}
|
|
30926
|
+
});
|
|
30927
|
+
}
|
|
30928
|
+
}
|
|
30929
|
+
return { enrollments };
|
|
30899
30930
|
}
|
|
30900
30931
|
async assertStudentEnrolledInCourse(client, studentId, courseId) {
|
|
30901
|
-
const
|
|
30932
|
+
const enrollments = await client.edubridge.enrollments.listByUser(studentId);
|
|
30933
|
+
const match = enrollments.find((e) => e.course.id === courseId);
|
|
30934
|
+
if (!match) {
|
|
30935
|
+
throw new NotFoundError("Student enrollment", `${studentId}:${courseId}`);
|
|
30936
|
+
}
|
|
30937
|
+
return match;
|
|
30938
|
+
}
|
|
30939
|
+
async assertStudentHasEnrollmentInCourse(client, studentId, courseId) {
|
|
30940
|
+
const { enrollments: enrollmentsByCourseId } = await this.getStudentEnrollmentsByCourseId(client, studentId, [courseId], {
|
|
30941
|
+
includeInactive: true
|
|
30942
|
+
});
|
|
30902
30943
|
if (!enrollmentsByCourseId.has(courseId)) {
|
|
30903
30944
|
throw new NotFoundError("Student enrollment", `${studentId}:${courseId}`);
|
|
30904
30945
|
}
|
|
@@ -30951,7 +30992,7 @@ class TimebackAdminService {
|
|
|
30951
30992
|
return [];
|
|
30952
30993
|
}
|
|
30953
30994
|
}
|
|
30954
|
-
async listStudentsForCourse(gameId, courseId, user) {
|
|
30995
|
+
async listStudentsForCourse(gameId, courseId, user, options) {
|
|
30955
30996
|
const client = this.requireClient();
|
|
30956
30997
|
await this.deps.validateGameManagementAccess(user, gameId);
|
|
30957
30998
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
@@ -30961,7 +31002,8 @@ class TimebackAdminService {
|
|
|
30961
31002
|
throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
|
|
30962
31003
|
}
|
|
30963
31004
|
const roster = await client.oneroster.enrollments.listByCourse(courseId, {
|
|
30964
|
-
role: "student"
|
|
31005
|
+
role: "student",
|
|
31006
|
+
includeInactive: options?.includeInactive
|
|
30965
31007
|
});
|
|
30966
31008
|
const analyticsByEnrollmentId = await this.loadEnrollmentAnalyticsSummaries(roster.map((rosterEntry) => rosterEntry.enrollment.sourcedId).filter((enrollmentId) => Boolean(enrollmentId)));
|
|
30967
31009
|
const masterableUnits = await this.getMasterableUnits(courseId);
|
|
@@ -30970,6 +31012,7 @@ class TimebackAdminService {
|
|
|
30970
31012
|
const summary = enrollmentId ? analyticsByEnrollmentId.get(enrollmentId) : undefined;
|
|
30971
31013
|
const analyticsUnavailable = Boolean(enrollmentId) && summary?.analyticsAvailable !== true;
|
|
30972
31014
|
const name3 = rosterEntry.user ? `${rosterEntry.user.givenName} ${rosterEntry.user.familyName}`.trim() : rosterEntry.enrollment.user.sourcedId;
|
|
31015
|
+
const inactive = rosterEntry.enrollment.status === "tobedeleted";
|
|
30973
31016
|
return {
|
|
30974
31017
|
studentId: rosterEntry.enrollment.user.sourcedId,
|
|
30975
31018
|
enrollmentId,
|
|
@@ -30983,11 +31026,25 @@ class TimebackAdminService {
|
|
|
30983
31026
|
activeTimeSeconds: summary?.activeTimeSeconds ?? 0,
|
|
30984
31027
|
masteredUnits: summary?.masteredUnits ?? 0,
|
|
30985
31028
|
masterableUnits,
|
|
30986
|
-
pctCompleteApp: TimebackAdminService.computeCompletionPct(summary?.masteredUnits ?? 0, masterableUnits)
|
|
31029
|
+
pctCompleteApp: TimebackAdminService.computeCompletionPct(summary?.masteredUnits ?? 0, masterableUnits),
|
|
31030
|
+
...inactive ? { inactive } : {}
|
|
30987
31031
|
};
|
|
30988
31032
|
});
|
|
30989
|
-
|
|
30990
|
-
|
|
31033
|
+
const studentMap = new Map;
|
|
31034
|
+
for (const student of students) {
|
|
31035
|
+
const existing = studentMap.get(student.studentId);
|
|
31036
|
+
if (!existing || existing.inactive && !student.inactive) {
|
|
31037
|
+
studentMap.set(student.studentId, student);
|
|
31038
|
+
}
|
|
31039
|
+
}
|
|
31040
|
+
const deduped = [...studentMap.values()];
|
|
31041
|
+
deduped.sort((a, b) => {
|
|
31042
|
+
if (a.inactive !== b.inactive) {
|
|
31043
|
+
return a.inactive ? 1 : -1;
|
|
31044
|
+
}
|
|
31045
|
+
return a.name.localeCompare(b.name);
|
|
31046
|
+
});
|
|
31047
|
+
return { gameId, courseId, students: deduped };
|
|
30991
31048
|
}
|
|
30992
31049
|
async getStudentOverview(gameId, studentId, user, courseId) {
|
|
30993
31050
|
const client = this.requireClient();
|
|
@@ -30999,7 +31056,9 @@ class TimebackAdminService {
|
|
|
30999
31056
|
throw new NotFoundError("Timeback integration", gameId);
|
|
31000
31057
|
}
|
|
31001
31058
|
const courseIds = new Set(integrations.map((integration) => integration.courseId));
|
|
31002
|
-
const enrollmentsByCourseId = await this.getStudentEnrollmentsByCourseId(client, studentId, [...courseIds]
|
|
31059
|
+
const { enrollments: enrollmentsByCourseId } = await this.getStudentEnrollmentsByCourseId(client, studentId, [...courseIds], {
|
|
31060
|
+
includeInactive: true
|
|
31061
|
+
});
|
|
31003
31062
|
if (enrollmentsByCourseId.size === 0) {
|
|
31004
31063
|
throw new NotFoundError("Student enrollment", courseId ? `${studentId}:${courseId}` : `${studentId}:${gameId}`);
|
|
31005
31064
|
}
|
|
@@ -31014,6 +31073,7 @@ class TimebackAdminService {
|
|
|
31014
31073
|
const summary = enrollment ? analyticsByEnrollmentId.get(enrollment.id) : undefined;
|
|
31015
31074
|
const masterableUnits = masterableUnitsByCourse.get(integration.courseId);
|
|
31016
31075
|
const analyticsUnavailable = Boolean(enrollment?.id) && summary?.analyticsAvailable !== true;
|
|
31076
|
+
const inactive = enrollment?.status === "tobedeleted";
|
|
31017
31077
|
return {
|
|
31018
31078
|
courseId: integration.courseId,
|
|
31019
31079
|
title: enrollment?.course.title || `${integration.subject} Grade ${integration.grade}`,
|
|
@@ -31028,7 +31088,8 @@ class TimebackAdminService {
|
|
|
31028
31088
|
masterableUnits,
|
|
31029
31089
|
pctCompleteApp: TimebackAdminService.computeCompletionPct(summary?.masteredUnits ?? 0, masterableUnits),
|
|
31030
31090
|
completionStatus: completionStatusByCourse.get(integration.courseId) ?? "none",
|
|
31031
|
-
history: summary?.history ?? []
|
|
31091
|
+
history: summary?.history ?? [],
|
|
31092
|
+
...inactive ? { inactive } : {}
|
|
31032
31093
|
};
|
|
31033
31094
|
});
|
|
31034
31095
|
return {
|
|
@@ -31055,7 +31116,7 @@ class TimebackAdminService {
|
|
|
31055
31116
|
if (!integration) {
|
|
31056
31117
|
throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
|
|
31057
31118
|
}
|
|
31058
|
-
await this.
|
|
31119
|
+
await this.assertStudentHasEnrollmentInCourse(client, studentId, courseId);
|
|
31059
31120
|
const relevantCourseIds = new Set([courseId]);
|
|
31060
31121
|
const fetchLimit = Math.min(safeOffset + safeLimit + 1, TimebackAdminService.MAX_STUDENT_ACTIVITY_OFFSET + TimebackAdminService.MAX_STUDENT_ACTIVITY_LIMIT + 1);
|
|
31061
31122
|
const allActivities = await this.listRecentActivityForStudent(client, studentId, gameSource, relevantCourseIds, fetchLimit);
|
|
@@ -31076,7 +31137,7 @@ class TimebackAdminService {
|
|
|
31076
31137
|
if (!integration) {
|
|
31077
31138
|
throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
|
|
31078
31139
|
}
|
|
31079
|
-
await this.
|
|
31140
|
+
await this.assertStudentHasEnrollmentInCourse(client, studentId, courseId);
|
|
31080
31141
|
const events = await this.fetchCaliperEventsForStudent(client, studentId, gameSource, TimebackAdminService.MAX_RECENT_ACTIVITY_EVENT_FETCH);
|
|
31081
31142
|
const relevantCourseIds = new Set([courseId]);
|
|
31082
31143
|
let matchedEvents;
|
|
@@ -35683,7 +35744,7 @@ function createOneRosterNamespace(client) {
|
|
|
35683
35744
|
listByClass: async (classSourcedId, options) => {
|
|
35684
35745
|
const queryParams = new URLSearchParams;
|
|
35685
35746
|
const filters = [`class.sourcedId='${escapeFilterValue2(classSourcedId)}'`];
|
|
35686
|
-
if (options?.
|
|
35747
|
+
if (!options?.includeInactive) {
|
|
35687
35748
|
filters.push(`status='active'`);
|
|
35688
35749
|
}
|
|
35689
35750
|
queryParams.set("filter", filters.join(" AND "));
|
|
@@ -35718,7 +35779,7 @@ function createOneRosterNamespace(client) {
|
|
|
35718
35779
|
const queryParams = new URLSearchParams;
|
|
35719
35780
|
const classFilter = batch.map((classId) => `class.sourcedId='${escapeFilterValue2(classId)}'`).join(" OR ");
|
|
35720
35781
|
const filters = [batch.length > 1 ? `(${classFilter})` : classFilter];
|
|
35721
|
-
if (options?.
|
|
35782
|
+
if (!options?.includeInactive) {
|
|
35722
35783
|
filters.push(`status='active'`);
|
|
35723
35784
|
}
|
|
35724
35785
|
if (options?.role) {
|
|
@@ -96845,15 +96906,19 @@ var init_timeback_controller = __esm(() => {
|
|
|
96845
96906
|
getRoster = requireGameManagementAccess(async (ctx) => {
|
|
96846
96907
|
const gameId = ctx.params.gameId;
|
|
96847
96908
|
const courseId = ctx.params.courseId;
|
|
96909
|
+
const includeInactive = ctx.url.searchParams.get("includeInactive") === "true";
|
|
96848
96910
|
if (!gameId || !courseId) {
|
|
96849
96911
|
throw ApiError.badRequest("Missing gameId or courseId parameter");
|
|
96850
96912
|
}
|
|
96851
96913
|
logger64.debug("Getting course roster", {
|
|
96852
96914
|
requesterId: ctx.user.id,
|
|
96853
96915
|
gameId,
|
|
96854
|
-
courseId
|
|
96916
|
+
courseId,
|
|
96917
|
+
includeInactive
|
|
96918
|
+
});
|
|
96919
|
+
return ctx.services.timebackAdmin.listStudentsForCourse(gameId, courseId, ctx.user, {
|
|
96920
|
+
includeInactive
|
|
96855
96921
|
});
|
|
96856
|
-
return ctx.services.timebackAdmin.listStudentsForCourse(gameId, courseId, ctx.user);
|
|
96857
96922
|
});
|
|
96858
96923
|
getStudentOverview = requireGameManagementAccess(async (ctx) => {
|
|
96859
96924
|
const timebackId = ctx.params.timebackId;
|