@playcademy/sandbox 0.3.17-beta.21 → 0.3.17-beta.22
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 +77 -11
- package/dist/server.js +77 -11
- 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.22",
|
|
1334
1334
|
description: "Local development server for Playcademy game development",
|
|
1335
1335
|
type: "module",
|
|
1336
1336
|
exports: {
|
|
@@ -30922,21 +30922,25 @@ class TimebackAdminService {
|
|
|
30922
30922
|
});
|
|
30923
30923
|
return new Map(results);
|
|
30924
30924
|
}
|
|
30925
|
+
async fetchCaliperEventsForStudent(client, studentId, source, limit) {
|
|
30926
|
+
const actorId = `${client.getBaseUrl().replace(/\/$/, "")}/ims/oneroster/rostering/v1p2/users/${studentId}`;
|
|
30927
|
+
const { events } = await client.caliper.events.list({
|
|
30928
|
+
limit,
|
|
30929
|
+
actorId,
|
|
30930
|
+
...source.sourceMode === "production" ? { sensor: source.sensorUrl } : {},
|
|
30931
|
+
extensions: {
|
|
30932
|
+
gameId: source.gameId
|
|
30933
|
+
}
|
|
30934
|
+
});
|
|
30935
|
+
return events;
|
|
30936
|
+
}
|
|
30925
30937
|
async listRecentActivityForStudent(client, studentId, source, relevantCourseIds, maxResults = TimebackAdminService.RECENT_ACTIVITY_LIMIT) {
|
|
30926
30938
|
if (relevantCourseIds.size === 0) {
|
|
30927
30939
|
return [];
|
|
30928
30940
|
}
|
|
30929
30941
|
try {
|
|
30930
|
-
const actorId = `${client.getBaseUrl().replace(/\/$/, "")}/ims/oneroster/rostering/v1p2/users/${studentId}`;
|
|
30931
30942
|
const eventLimit = Math.min(Math.max(200, maxResults * 20), TimebackAdminService.MAX_RECENT_ACTIVITY_EVENT_FETCH);
|
|
30932
|
-
const
|
|
30933
|
-
limit: eventLimit,
|
|
30934
|
-
actorId,
|
|
30935
|
-
...source.sourceMode === "production" ? { sensor: source.sensorUrl } : {},
|
|
30936
|
-
extensions: {
|
|
30937
|
-
gameId: source.gameId
|
|
30938
|
-
}
|
|
30939
|
-
});
|
|
30943
|
+
const events = await this.fetchCaliperEventsForStudent(client, studentId, source, eventLimit);
|
|
30940
30944
|
return TimebackAdminService.mapRecentActivityItems(events, relevantCourseIds).slice(0, maxResults);
|
|
30941
30945
|
} catch (error) {
|
|
30942
30946
|
logger16.warn("Failed to load recent Caliper activity", {
|
|
@@ -31060,6 +31064,42 @@ class TimebackAdminService {
|
|
|
31060
31064
|
const hasMore = allActivities.length > safeOffset + safeLimit;
|
|
31061
31065
|
return { activities, hasMore };
|
|
31062
31066
|
}
|
|
31067
|
+
async getActivityDetail(user, options) {
|
|
31068
|
+
const { gameId, studentId, courseId, activityId, runId } = options;
|
|
31069
|
+
const client = this.requireClient();
|
|
31070
|
+
await this.deps.validateGameManagementAccess(user, gameId);
|
|
31071
|
+
const [integration, gameSource] = await Promise.all([
|
|
31072
|
+
this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
31073
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
31074
|
+
}),
|
|
31075
|
+
this.getGameActivitySource(gameId)
|
|
31076
|
+
]);
|
|
31077
|
+
if (!integration) {
|
|
31078
|
+
throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
|
|
31079
|
+
}
|
|
31080
|
+
await this.assertStudentEnrolledInCourse(client, studentId, courseId);
|
|
31081
|
+
const events = await this.fetchCaliperEventsForStudent(client, studentId, gameSource, TimebackAdminService.MAX_RECENT_ACTIVITY_EVENT_FETCH);
|
|
31082
|
+
const relevantCourseIds = new Set([courseId]);
|
|
31083
|
+
let matchedEvents;
|
|
31084
|
+
let activity;
|
|
31085
|
+
if (runId) {
|
|
31086
|
+
const gameplayEvents = events.filter((event) => (event.type === "ActivityEvent" || event.type === "TimeSpentEvent") && !isCaliperRemediationOrCompletionEvent(event));
|
|
31087
|
+
const groups = groupCaliperEventsByRun(gameplayEvents);
|
|
31088
|
+
matchedEvents = [...groups.values()].find((group) => group.some((event) => getCanonicalRunId(event.session) === runId && event.externalId === activityId)) ?? [];
|
|
31089
|
+
activity = mapCaliperEventGroupToActivity(matchedEvents, relevantCourseIds);
|
|
31090
|
+
} else {
|
|
31091
|
+
matchedEvents = events.filter((event) => event.externalId === activityId);
|
|
31092
|
+
if (matchedEvents.length > 0) {
|
|
31093
|
+
activity = mapCaliperEventToRemediationActivity(matchedEvents[0], relevantCourseIds) ?? mapCaliperEventGroupToActivity(matchedEvents, relevantCourseIds);
|
|
31094
|
+
} else {
|
|
31095
|
+
activity = null;
|
|
31096
|
+
}
|
|
31097
|
+
}
|
|
31098
|
+
if (!activity) {
|
|
31099
|
+
throw new NotFoundError("Activity", activityId);
|
|
31100
|
+
}
|
|
31101
|
+
return { activity, rawEvents: matchedEvents };
|
|
31102
|
+
}
|
|
31063
31103
|
async grantManualXp(data, user) {
|
|
31064
31104
|
const { client, sensorUrl, appName, actor } = await this.resolveAdminMutationContext(data.gameId, data.courseId, user, data.studentId);
|
|
31065
31105
|
await client.recordAdminXpAdjustment({
|
|
@@ -96522,7 +96562,7 @@ var init_sprite_controller = __esm(() => {
|
|
|
96522
96562
|
});
|
|
96523
96563
|
|
|
96524
96564
|
// ../api-core/src/controllers/timeback.controller.ts
|
|
96525
|
-
var logger64, getTodayXp, getTotalXp, updateTodayXp, getXpHistory, populateStudent, getUser, getUserById, setupIntegration, getIntegrations, verifyIntegration, getConfig2, deleteIntegrations, endActivity, heartbeat, advanceCourse, getStudentXp, getRoster, getStudentOverview, getStudentActivity, grantXp, adjustTime, adjustMastery, toggleCompletion, searchStudents, enrollStudent, unenrollStudent, timeback2;
|
|
96565
|
+
var logger64, getTodayXp, getTotalXp, updateTodayXp, getXpHistory, populateStudent, getUser, getUserById, setupIntegration, getIntegrations, verifyIntegration, getConfig2, deleteIntegrations, endActivity, heartbeat, advanceCourse, getStudentXp, getRoster, getStudentOverview, getStudentActivity, getActivityDetail, grantXp, adjustTime, adjustMastery, toggleCompletion, searchStudents, enrollStudent, unenrollStudent, timeback2;
|
|
96526
96566
|
var init_timeback_controller = __esm(() => {
|
|
96527
96567
|
init_esm();
|
|
96528
96568
|
init_schemas_index();
|
|
@@ -96858,6 +96898,31 @@ var init_timeback_controller = __esm(() => {
|
|
|
96858
96898
|
offset
|
|
96859
96899
|
});
|
|
96860
96900
|
});
|
|
96901
|
+
getActivityDetail = requireGameManagementAccess(async (ctx) => {
|
|
96902
|
+
const timebackId = ctx.params.timebackId;
|
|
96903
|
+
const courseId = ctx.params.courseId;
|
|
96904
|
+
const activityId = ctx.params.activityId;
|
|
96905
|
+
const gameId = ctx.url.searchParams.get("gameId") || undefined;
|
|
96906
|
+
const runId = ctx.url.searchParams.get("runId") || undefined;
|
|
96907
|
+
if (!timebackId || !courseId || !activityId || !gameId) {
|
|
96908
|
+
throw ApiError.badRequest("Missing timebackId, courseId, or activityId path parameter, or gameId query parameter");
|
|
96909
|
+
}
|
|
96910
|
+
logger64.debug("Getting activity detail", {
|
|
96911
|
+
requesterId: ctx.user.id,
|
|
96912
|
+
timebackId,
|
|
96913
|
+
courseId,
|
|
96914
|
+
activityId,
|
|
96915
|
+
gameId,
|
|
96916
|
+
runId
|
|
96917
|
+
});
|
|
96918
|
+
return ctx.services.timebackAdmin.getActivityDetail(ctx.user, {
|
|
96919
|
+
gameId,
|
|
96920
|
+
studentId: timebackId,
|
|
96921
|
+
courseId,
|
|
96922
|
+
activityId,
|
|
96923
|
+
runId
|
|
96924
|
+
});
|
|
96925
|
+
});
|
|
96861
96926
|
grantXp = requireDeveloper(async (ctx) => {
|
|
96862
96927
|
const body2 = await parseRequestBody(ctx.request, GrantTimebackXpRequestSchema);
|
|
96863
96928
|
logger64.debug("Granting manual XP", {
|
|
@@ -96960,6 +97025,7 @@ var init_timeback_controller = __esm(() => {
|
|
|
96960
97025
|
getRoster,
|
|
96961
97026
|
getStudentOverview,
|
|
96962
97027
|
getStudentActivity,
|
|
97028
|
+
getActivityDetail,
|
|
96963
97029
|
grantXp,
|
|
96964
97030
|
adjustTime,
|
|
96965
97031
|
adjustMastery,
|
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.22",
|
|
1333
1333
|
description: "Local development server for Playcademy game development",
|
|
1334
1334
|
type: "module",
|
|
1335
1335
|
exports: {
|
|
@@ -30921,21 +30921,25 @@ class TimebackAdminService {
|
|
|
30921
30921
|
});
|
|
30922
30922
|
return new Map(results);
|
|
30923
30923
|
}
|
|
30924
|
+
async fetchCaliperEventsForStudent(client, studentId, source, limit) {
|
|
30925
|
+
const actorId = `${client.getBaseUrl().replace(/\/$/, "")}/ims/oneroster/rostering/v1p2/users/${studentId}`;
|
|
30926
|
+
const { events } = await client.caliper.events.list({
|
|
30927
|
+
limit,
|
|
30928
|
+
actorId,
|
|
30929
|
+
...source.sourceMode === "production" ? { sensor: source.sensorUrl } : {},
|
|
30930
|
+
extensions: {
|
|
30931
|
+
gameId: source.gameId
|
|
30932
|
+
}
|
|
30933
|
+
});
|
|
30934
|
+
return events;
|
|
30935
|
+
}
|
|
30924
30936
|
async listRecentActivityForStudent(client, studentId, source, relevantCourseIds, maxResults = TimebackAdminService.RECENT_ACTIVITY_LIMIT) {
|
|
30925
30937
|
if (relevantCourseIds.size === 0) {
|
|
30926
30938
|
return [];
|
|
30927
30939
|
}
|
|
30928
30940
|
try {
|
|
30929
|
-
const actorId = `${client.getBaseUrl().replace(/\/$/, "")}/ims/oneroster/rostering/v1p2/users/${studentId}`;
|
|
30930
30941
|
const eventLimit = Math.min(Math.max(200, maxResults * 20), TimebackAdminService.MAX_RECENT_ACTIVITY_EVENT_FETCH);
|
|
30931
|
-
const
|
|
30932
|
-
limit: eventLimit,
|
|
30933
|
-
actorId,
|
|
30934
|
-
...source.sourceMode === "production" ? { sensor: source.sensorUrl } : {},
|
|
30935
|
-
extensions: {
|
|
30936
|
-
gameId: source.gameId
|
|
30937
|
-
}
|
|
30938
|
-
});
|
|
30942
|
+
const events = await this.fetchCaliperEventsForStudent(client, studentId, source, eventLimit);
|
|
30939
30943
|
return TimebackAdminService.mapRecentActivityItems(events, relevantCourseIds).slice(0, maxResults);
|
|
30940
30944
|
} catch (error) {
|
|
30941
30945
|
logger16.warn("Failed to load recent Caliper activity", {
|
|
@@ -31059,6 +31063,42 @@ class TimebackAdminService {
|
|
|
31059
31063
|
const hasMore = allActivities.length > safeOffset + safeLimit;
|
|
31060
31064
|
return { activities, hasMore };
|
|
31061
31065
|
}
|
|
31066
|
+
async getActivityDetail(user, options) {
|
|
31067
|
+
const { gameId, studentId, courseId, activityId, runId } = options;
|
|
31068
|
+
const client = this.requireClient();
|
|
31069
|
+
await this.deps.validateGameManagementAccess(user, gameId);
|
|
31070
|
+
const [integration, gameSource] = await Promise.all([
|
|
31071
|
+
this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
31072
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
31073
|
+
}),
|
|
31074
|
+
this.getGameActivitySource(gameId)
|
|
31075
|
+
]);
|
|
31076
|
+
if (!integration) {
|
|
31077
|
+
throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
|
|
31078
|
+
}
|
|
31079
|
+
await this.assertStudentEnrolledInCourse(client, studentId, courseId);
|
|
31080
|
+
const events = await this.fetchCaliperEventsForStudent(client, studentId, gameSource, TimebackAdminService.MAX_RECENT_ACTIVITY_EVENT_FETCH);
|
|
31081
|
+
const relevantCourseIds = new Set([courseId]);
|
|
31082
|
+
let matchedEvents;
|
|
31083
|
+
let activity;
|
|
31084
|
+
if (runId) {
|
|
31085
|
+
const gameplayEvents = events.filter((event) => (event.type === "ActivityEvent" || event.type === "TimeSpentEvent") && !isCaliperRemediationOrCompletionEvent(event));
|
|
31086
|
+
const groups = groupCaliperEventsByRun(gameplayEvents);
|
|
31087
|
+
matchedEvents = [...groups.values()].find((group) => group.some((event) => getCanonicalRunId(event.session) === runId && event.externalId === activityId)) ?? [];
|
|
31088
|
+
activity = mapCaliperEventGroupToActivity(matchedEvents, relevantCourseIds);
|
|
31089
|
+
} else {
|
|
31090
|
+
matchedEvents = events.filter((event) => event.externalId === activityId);
|
|
31091
|
+
if (matchedEvents.length > 0) {
|
|
31092
|
+
activity = mapCaliperEventToRemediationActivity(matchedEvents[0], relevantCourseIds) ?? mapCaliperEventGroupToActivity(matchedEvents, relevantCourseIds);
|
|
31093
|
+
} else {
|
|
31094
|
+
activity = null;
|
|
31095
|
+
}
|
|
31096
|
+
}
|
|
31097
|
+
if (!activity) {
|
|
31098
|
+
throw new NotFoundError("Activity", activityId);
|
|
31099
|
+
}
|
|
31100
|
+
return { activity, rawEvents: matchedEvents };
|
|
31101
|
+
}
|
|
31062
31102
|
async grantManualXp(data, user) {
|
|
31063
31103
|
const { client, sensorUrl, appName, actor } = await this.resolveAdminMutationContext(data.gameId, data.courseId, user, data.studentId);
|
|
31064
31104
|
await client.recordAdminXpAdjustment({
|
|
@@ -96521,7 +96561,7 @@ var init_sprite_controller = __esm(() => {
|
|
|
96521
96561
|
});
|
|
96522
96562
|
|
|
96523
96563
|
// ../api-core/src/controllers/timeback.controller.ts
|
|
96524
|
-
var logger64, getTodayXp, getTotalXp, updateTodayXp, getXpHistory, populateStudent, getUser, getUserById, setupIntegration, getIntegrations, verifyIntegration, getConfig2, deleteIntegrations, endActivity, heartbeat, advanceCourse, getStudentXp, getRoster, getStudentOverview, getStudentActivity, grantXp, adjustTime, adjustMastery, toggleCompletion, searchStudents, enrollStudent, unenrollStudent, timeback2;
|
|
96564
|
+
var logger64, getTodayXp, getTotalXp, updateTodayXp, getXpHistory, populateStudent, getUser, getUserById, setupIntegration, getIntegrations, verifyIntegration, getConfig2, deleteIntegrations, endActivity, heartbeat, advanceCourse, getStudentXp, getRoster, getStudentOverview, getStudentActivity, getActivityDetail, grantXp, adjustTime, adjustMastery, toggleCompletion, searchStudents, enrollStudent, unenrollStudent, timeback2;
|
|
96525
96565
|
var init_timeback_controller = __esm(() => {
|
|
96526
96566
|
init_esm();
|
|
96527
96567
|
init_schemas_index();
|
|
@@ -96857,6 +96897,31 @@ var init_timeback_controller = __esm(() => {
|
|
|
96857
96897
|
offset
|
|
96858
96898
|
});
|
|
96859
96899
|
});
|
|
96900
|
+
getActivityDetail = requireGameManagementAccess(async (ctx) => {
|
|
96901
|
+
const timebackId = ctx.params.timebackId;
|
|
96902
|
+
const courseId = ctx.params.courseId;
|
|
96903
|
+
const activityId = ctx.params.activityId;
|
|
96904
|
+
const gameId = ctx.url.searchParams.get("gameId") || undefined;
|
|
96905
|
+
const runId = ctx.url.searchParams.get("runId") || undefined;
|
|
96906
|
+
if (!timebackId || !courseId || !activityId || !gameId) {
|
|
96907
|
+
throw ApiError.badRequest("Missing timebackId, courseId, or activityId path parameter, or gameId query parameter");
|
|
96908
|
+
}
|
|
96909
|
+
logger64.debug("Getting activity detail", {
|
|
96910
|
+
requesterId: ctx.user.id,
|
|
96911
|
+
timebackId,
|
|
96912
|
+
courseId,
|
|
96913
|
+
activityId,
|
|
96914
|
+
gameId,
|
|
96915
|
+
runId
|
|
96916
|
+
});
|
|
96917
|
+
return ctx.services.timebackAdmin.getActivityDetail(ctx.user, {
|
|
96918
|
+
gameId,
|
|
96919
|
+
studentId: timebackId,
|
|
96920
|
+
courseId,
|
|
96921
|
+
activityId,
|
|
96922
|
+
runId
|
|
96923
|
+
});
|
|
96924
|
+
});
|
|
96860
96925
|
grantXp = requireDeveloper(async (ctx) => {
|
|
96861
96926
|
const body2 = await parseRequestBody(ctx.request, GrantTimebackXpRequestSchema);
|
|
96862
96927
|
logger64.debug("Granting manual XP", {
|
|
@@ -96959,6 +97024,7 @@ var init_timeback_controller = __esm(() => {
|
|
|
96959
97024
|
getRoster,
|
|
96960
97025
|
getStudentOverview,
|
|
96961
97026
|
getStudentActivity,
|
|
97027
|
+
getActivityDetail,
|
|
96962
97028
|
grantXp,
|
|
96963
97029
|
adjustTime,
|
|
96964
97030
|
adjustMastery,
|