@playcademy/vite-plugin 0.2.26-beta.5 → 0.2.26-beta.7
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/index.js +126 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -25370,7 +25370,7 @@ var package_default;
|
|
|
25370
25370
|
var init_package = __esm(() => {
|
|
25371
25371
|
package_default = {
|
|
25372
25372
|
name: "@playcademy/sandbox",
|
|
25373
|
-
version: "0.3.17-beta.
|
|
25373
|
+
version: "0.3.17-beta.22",
|
|
25374
25374
|
description: "Local development server for Playcademy game development",
|
|
25375
25375
|
type: "module",
|
|
25376
25376
|
exports: {
|
|
@@ -54822,21 +54822,25 @@ class TimebackAdminService {
|
|
|
54822
54822
|
});
|
|
54823
54823
|
return new Map(results);
|
|
54824
54824
|
}
|
|
54825
|
+
async fetchCaliperEventsForStudent(client, studentId, source, limit) {
|
|
54826
|
+
const actorId = `${client.getBaseUrl().replace(/\/$/, "")}/ims/oneroster/rostering/v1p2/users/${studentId}`;
|
|
54827
|
+
const { events } = await client.caliper.events.list({
|
|
54828
|
+
limit,
|
|
54829
|
+
actorId,
|
|
54830
|
+
...source.sourceMode === "production" ? { sensor: source.sensorUrl } : {},
|
|
54831
|
+
extensions: {
|
|
54832
|
+
gameId: source.gameId
|
|
54833
|
+
}
|
|
54834
|
+
});
|
|
54835
|
+
return events;
|
|
54836
|
+
}
|
|
54825
54837
|
async listRecentActivityForStudent(client, studentId, source, relevantCourseIds, maxResults = TimebackAdminService.RECENT_ACTIVITY_LIMIT) {
|
|
54826
54838
|
if (relevantCourseIds.size === 0) {
|
|
54827
54839
|
return [];
|
|
54828
54840
|
}
|
|
54829
54841
|
try {
|
|
54830
|
-
const actorId = `${client.getBaseUrl().replace(/\/$/, "")}/ims/oneroster/rostering/v1p2/users/${studentId}`;
|
|
54831
54842
|
const eventLimit = Math.min(Math.max(200, maxResults * 20), TimebackAdminService.MAX_RECENT_ACTIVITY_EVENT_FETCH);
|
|
54832
|
-
const
|
|
54833
|
-
limit: eventLimit,
|
|
54834
|
-
actorId,
|
|
54835
|
-
...source.sourceMode === "production" ? { sensor: source.sensorUrl } : {},
|
|
54836
|
-
extensions: {
|
|
54837
|
-
gameId: source.gameId
|
|
54838
|
-
}
|
|
54839
|
-
});
|
|
54843
|
+
const events = await this.fetchCaliperEventsForStudent(client, studentId, source, eventLimit);
|
|
54840
54844
|
return TimebackAdminService.mapRecentActivityItems(events, relevantCourseIds).slice(0, maxResults);
|
|
54841
54845
|
} catch (error) {
|
|
54842
54846
|
logger16.warn("Failed to load recent Caliper activity", {
|
|
@@ -54960,6 +54964,42 @@ class TimebackAdminService {
|
|
|
54960
54964
|
const hasMore = allActivities.length > safeOffset + safeLimit;
|
|
54961
54965
|
return { activities, hasMore };
|
|
54962
54966
|
}
|
|
54967
|
+
async getActivityDetail(user, options) {
|
|
54968
|
+
const { gameId, studentId, courseId, activityId, runId } = options;
|
|
54969
|
+
const client = this.requireClient();
|
|
54970
|
+
await this.deps.validateGameManagementAccess(user, gameId);
|
|
54971
|
+
const [integration, gameSource] = await Promise.all([
|
|
54972
|
+
this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
54973
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
54974
|
+
}),
|
|
54975
|
+
this.getGameActivitySource(gameId)
|
|
54976
|
+
]);
|
|
54977
|
+
if (!integration) {
|
|
54978
|
+
throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
|
|
54979
|
+
}
|
|
54980
|
+
await this.assertStudentEnrolledInCourse(client, studentId, courseId);
|
|
54981
|
+
const events = await this.fetchCaliperEventsForStudent(client, studentId, gameSource, TimebackAdminService.MAX_RECENT_ACTIVITY_EVENT_FETCH);
|
|
54982
|
+
const relevantCourseIds = new Set([courseId]);
|
|
54983
|
+
let matchedEvents;
|
|
54984
|
+
let activity;
|
|
54985
|
+
if (runId) {
|
|
54986
|
+
const gameplayEvents = events.filter((event) => (event.type === "ActivityEvent" || event.type === "TimeSpentEvent") && !isCaliperRemediationOrCompletionEvent(event));
|
|
54987
|
+
const groups = groupCaliperEventsByRun(gameplayEvents);
|
|
54988
|
+
matchedEvents = [...groups.values()].find((group) => group.some((event) => getCanonicalRunId(event.session) === runId && event.externalId === activityId)) ?? [];
|
|
54989
|
+
activity = mapCaliperEventGroupToActivity(matchedEvents, relevantCourseIds);
|
|
54990
|
+
} else {
|
|
54991
|
+
matchedEvents = events.filter((event) => event.externalId === activityId);
|
|
54992
|
+
if (matchedEvents.length > 0) {
|
|
54993
|
+
activity = mapCaliperEventToRemediationActivity(matchedEvents[0], relevantCourseIds) ?? mapCaliperEventGroupToActivity(matchedEvents, relevantCourseIds);
|
|
54994
|
+
} else {
|
|
54995
|
+
activity = null;
|
|
54996
|
+
}
|
|
54997
|
+
}
|
|
54998
|
+
if (!activity) {
|
|
54999
|
+
throw new NotFoundError("Activity", activityId);
|
|
55000
|
+
}
|
|
55001
|
+
return { activity, rawEvents: matchedEvents };
|
|
55002
|
+
}
|
|
54963
55003
|
async grantManualXp(data, user) {
|
|
54964
55004
|
const { client, sensorUrl, appName, actor } = await this.resolveAdminMutationContext(data.gameId, data.courseId, user, data.studentId);
|
|
54965
55005
|
await client.recordAdminXpAdjustment({
|
|
@@ -60133,7 +60173,7 @@ class MasteryTracker {
|
|
|
60133
60173
|
}
|
|
60134
60174
|
async checkProgress(input) {
|
|
60135
60175
|
const { studentId, courseId, resourceId, masteredUnits } = input;
|
|
60136
|
-
if (typeof masteredUnits !== "number" || masteredUnits
|
|
60176
|
+
if (typeof masteredUnits !== "number" || masteredUnits === 0) {
|
|
60137
60177
|
return;
|
|
60138
60178
|
}
|
|
60139
60179
|
const status = await this.calculateStatus({
|
|
@@ -60145,9 +60185,11 @@ class MasteryTracker {
|
|
|
60145
60185
|
if (!status) {
|
|
60146
60186
|
return;
|
|
60147
60187
|
}
|
|
60188
|
+
const wasComplete = status.historicalMasteredUnits >= status.masterableUnits;
|
|
60148
60189
|
return {
|
|
60149
60190
|
pctCompleteApp: status.pctCompleteApp,
|
|
60150
|
-
masteryAchieved:
|
|
60191
|
+
masteryAchieved: !wasComplete && status.isComplete,
|
|
60192
|
+
masteryRevoked: wasComplete && !status.isComplete
|
|
60151
60193
|
};
|
|
60152
60194
|
}
|
|
60153
60195
|
async getStatus(input) {
|
|
@@ -60188,7 +60230,7 @@ class MasteryTracker {
|
|
|
60188
60230
|
return;
|
|
60189
60231
|
}
|
|
60190
60232
|
const historicalMasteredUnits = this.sumAnalyticsMetric(facts, "masteredUnits");
|
|
60191
|
-
const totalMastered = historicalMasteredUnits + additionalMasteredUnits;
|
|
60233
|
+
const totalMastered = Math.max(0, historicalMasteredUnits + additionalMasteredUnits);
|
|
60192
60234
|
const rawPct = totalMastered / masterableUnits * 100;
|
|
60193
60235
|
const pctCompleteApp = Math.min(100, Math.max(0, Math.round(rawPct)));
|
|
60194
60236
|
return {
|
|
@@ -60238,6 +60280,45 @@ class MasteryTracker {
|
|
|
60238
60280
|
});
|
|
60239
60281
|
}
|
|
60240
60282
|
}
|
|
60283
|
+
async revokeCompletionEntry(studentId, courseId, classId, appName) {
|
|
60284
|
+
const ids = deriveSourcedIds2(courseId);
|
|
60285
|
+
const lineItemId = `${ids.course}-mastery-completion-assessment`;
|
|
60286
|
+
const resultId = `${lineItemId}:${studentId}:completion`;
|
|
60287
|
+
try {
|
|
60288
|
+
await this.onerosterNamespace.assessmentLineItems.findOrCreate(lineItemId, {
|
|
60289
|
+
sourcedId: lineItemId,
|
|
60290
|
+
title: "Mastery Completion",
|
|
60291
|
+
status: ONEROSTER_STATUS4.active,
|
|
60292
|
+
...classId ? { class: { sourcedId: classId } } : { course: { sourcedId: ids.course } },
|
|
60293
|
+
...ids.componentResource ? { componentResource: { sourcedId: ids.componentResource } } : {}
|
|
60294
|
+
});
|
|
60295
|
+
await this.onerosterNamespace.assessmentResults.upsert(resultId, {
|
|
60296
|
+
sourcedId: resultId,
|
|
60297
|
+
status: ONEROSTER_STATUS4.active,
|
|
60298
|
+
assessmentLineItem: { sourcedId: lineItemId },
|
|
60299
|
+
student: { sourcedId: studentId },
|
|
60300
|
+
score: 0,
|
|
60301
|
+
scoreDate: new Date().toISOString(),
|
|
60302
|
+
scoreStatus: SCORE_STATUS4.notSubmitted,
|
|
60303
|
+
inProgress: "true",
|
|
60304
|
+
metadata: {
|
|
60305
|
+
isMasteryCompletion: true,
|
|
60306
|
+
appName
|
|
60307
|
+
}
|
|
60308
|
+
});
|
|
60309
|
+
log.info("[MasteryTracker] Revoked mastery completion entry", {
|
|
60310
|
+
studentId,
|
|
60311
|
+
lineItemId,
|
|
60312
|
+
resultId
|
|
60313
|
+
});
|
|
60314
|
+
} catch (error) {
|
|
60315
|
+
log.error("[MasteryTracker] Failed to revoke mastery completion entry", {
|
|
60316
|
+
studentId,
|
|
60317
|
+
lineItemId,
|
|
60318
|
+
error
|
|
60319
|
+
});
|
|
60320
|
+
}
|
|
60321
|
+
}
|
|
60241
60322
|
async resolveMasterableUnits(resourceId) {
|
|
60242
60323
|
if (!resourceId) {
|
|
60243
60324
|
return;
|
|
@@ -60447,6 +60528,9 @@ class ProgressRecorder {
|
|
|
60447
60528
|
sensorUrl: progressData.sensorUrl
|
|
60448
60529
|
});
|
|
60449
60530
|
}
|
|
60531
|
+
if (masteryProgress?.masteryRevoked) {
|
|
60532
|
+
await this.masteryTracker.revokeCompletionEntry(studentId, courseId, progressData.classId, progressData.appName);
|
|
60533
|
+
}
|
|
60450
60534
|
await this.emitCaliperEvent({
|
|
60451
60535
|
studentId,
|
|
60452
60536
|
studentEmail,
|
|
@@ -120742,7 +120826,7 @@ var init_schemas11 = __esm(() => {
|
|
|
120742
120826
|
inactiveSeconds: exports_external.number().nonnegative().optional()
|
|
120743
120827
|
}).optional(),
|
|
120744
120828
|
xpEarned: exports_external.number().optional(),
|
|
120745
|
-
masteredUnits: exports_external.number().
|
|
120829
|
+
masteredUnits: exports_external.number().optional(),
|
|
120746
120830
|
extensions: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
|
|
120747
120831
|
});
|
|
120748
120832
|
AdvanceCourseRequestSchema = exports_external.object({
|
|
@@ -123103,6 +123187,7 @@ var getStudentXp;
|
|
|
123103
123187
|
var getRoster;
|
|
123104
123188
|
var getStudentOverview;
|
|
123105
123189
|
var getStudentActivity;
|
|
123190
|
+
var getActivityDetail;
|
|
123106
123191
|
var grantXp;
|
|
123107
123192
|
var adjustTime;
|
|
123108
123193
|
var adjustMastery;
|
|
@@ -123446,6 +123531,31 @@ var init_timeback_controller = __esm(() => {
|
|
|
123446
123531
|
offset
|
|
123447
123532
|
});
|
|
123448
123533
|
});
|
|
123534
|
+
getActivityDetail = requireGameManagementAccess(async (ctx) => {
|
|
123535
|
+
const timebackId = ctx.params.timebackId;
|
|
123536
|
+
const courseId = ctx.params.courseId;
|
|
123537
|
+
const activityId = ctx.params.activityId;
|
|
123538
|
+
const gameId = ctx.url.searchParams.get("gameId") || undefined;
|
|
123539
|
+
const runId = ctx.url.searchParams.get("runId") || undefined;
|
|
123540
|
+
if (!timebackId || !courseId || !activityId || !gameId) {
|
|
123541
|
+
throw ApiError.badRequest("Missing timebackId, courseId, or activityId path parameter, or gameId query parameter");
|
|
123542
|
+
}
|
|
123543
|
+
logger64.debug("Getting activity detail", {
|
|
123544
|
+
requesterId: ctx.user.id,
|
|
123545
|
+
timebackId,
|
|
123546
|
+
courseId,
|
|
123547
|
+
activityId,
|
|
123548
|
+
gameId,
|
|
123549
|
+
runId
|
|
123550
|
+
});
|
|
123551
|
+
return ctx.services.timebackAdmin.getActivityDetail(ctx.user, {
|
|
123552
|
+
gameId,
|
|
123553
|
+
studentId: timebackId,
|
|
123554
|
+
courseId,
|
|
123555
|
+
activityId,
|
|
123556
|
+
runId
|
|
123557
|
+
});
|
|
123558
|
+
});
|
|
123449
123559
|
grantXp = requireDeveloper(async (ctx) => {
|
|
123450
123560
|
const body2 = await parseRequestBody(ctx.request, GrantTimebackXpRequestSchema);
|
|
123451
123561
|
logger64.debug("Granting manual XP", {
|
|
@@ -123548,6 +123658,7 @@ var init_timeback_controller = __esm(() => {
|
|
|
123548
123658
|
getRoster,
|
|
123549
123659
|
getStudentOverview,
|
|
123550
123660
|
getStudentActivity,
|
|
123661
|
+
getActivityDetail,
|
|
123551
123662
|
grantXp,
|
|
123552
123663
|
adjustTime,
|
|
123553
123664
|
adjustMastery,
|
|
@@ -126369,7 +126480,7 @@ var import_picocolors12 = __toESM(require_picocolors(), 1);
|
|
|
126369
126480
|
// package.json
|
|
126370
126481
|
var package_default2 = {
|
|
126371
126482
|
name: "@playcademy/vite-plugin",
|
|
126372
|
-
version: "0.2.26-beta.
|
|
126483
|
+
version: "0.2.26-beta.7",
|
|
126373
126484
|
type: "module",
|
|
126374
126485
|
exports: {
|
|
126375
126486
|
".": {
|