@playcademy/vite-plugin 0.3.2-beta.3 → 0.3.3-beta.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/index.js +255 -15
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -23746,7 +23746,7 @@ import path from "node:path";
|
|
|
23746
23746
|
// package.json
|
|
23747
23747
|
var package_default = {
|
|
23748
23748
|
name: "@playcademy/vite-plugin",
|
|
23749
|
-
version: "0.3.
|
|
23749
|
+
version: "0.3.3-beta.1",
|
|
23750
23750
|
type: "module",
|
|
23751
23751
|
exports: {
|
|
23752
23752
|
".": {
|
|
@@ -24343,6 +24343,7 @@ var init_timeback2 = __esm(() => {
|
|
|
24343
24343
|
TIMEBACK_ROUTES = {
|
|
24344
24344
|
END_ACTIVITY: "/integrations/timeback/end-activity",
|
|
24345
24345
|
GET_XP: "/integrations/timeback/xp",
|
|
24346
|
+
GET_MASTERY: "/integrations/timeback/mastery",
|
|
24346
24347
|
HEARTBEAT: "/integrations/timeback/heartbeat",
|
|
24347
24348
|
ADVANCE_COURSE: "/integrations/timeback/advance-course"
|
|
24348
24349
|
};
|
|
@@ -25176,7 +25177,7 @@ var package_default2;
|
|
|
25176
25177
|
var init_package = __esm(() => {
|
|
25177
25178
|
package_default2 = {
|
|
25178
25179
|
name: "@playcademy/sandbox",
|
|
25179
|
-
version: "0.
|
|
25180
|
+
version: "0.4.1-beta.1",
|
|
25180
25181
|
description: "Local development server for Playcademy game development",
|
|
25181
25182
|
type: "module",
|
|
25182
25183
|
exports: {
|
|
@@ -52124,6 +52125,7 @@ var init_constants3 = __esm(() => {
|
|
|
52124
52125
|
TIMEBACK: {
|
|
52125
52126
|
END_ACTIVITY: `/api${TIMEBACK_ROUTES.END_ACTIVITY}`,
|
|
52126
52127
|
GET_XP: `/api${TIMEBACK_ROUTES.GET_XP}`,
|
|
52128
|
+
GET_MASTERY: `/api${TIMEBACK_ROUTES.GET_MASTERY}`,
|
|
52127
52129
|
HEARTBEAT: `/api${TIMEBACK_ROUTES.HEARTBEAT}`,
|
|
52128
52130
|
ADVANCE_COURSE: `/api${TIMEBACK_ROUTES.ADVANCE_COURSE}`
|
|
52129
52131
|
}
|
|
@@ -53964,7 +53966,11 @@ var init_schemas4 = __esm(() => {
|
|
|
53964
53966
|
}).optional(),
|
|
53965
53967
|
xpEarned: exports_external.number().optional(),
|
|
53966
53968
|
masteredUnits: exports_external.number().optional(),
|
|
53969
|
+
masteredUnitsAbsolute: exports_external.number().int().nonnegative().optional(),
|
|
53967
53970
|
extensions: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
|
|
53971
|
+
}).refine((data) => !(data.masteredUnits !== undefined && data.masteredUnitsAbsolute !== undefined), {
|
|
53972
|
+
message: "Cannot provide both masteredUnits and masteredUnitsAbsolute",
|
|
53973
|
+
path: ["masteredUnitsAbsolute"]
|
|
53968
53974
|
});
|
|
53969
53975
|
GameActivityMetricsSchema = exports_external.object({
|
|
53970
53976
|
activityId: exports_external.string().min(1),
|
|
@@ -54325,7 +54331,7 @@ function mapEnrollmentsToUserEnrollments(enrollments, integrations) {
|
|
|
54325
54331
|
subject: integration.subject,
|
|
54326
54332
|
courseId: integration.courseId,
|
|
54327
54333
|
orgId: courseToSchool.get(integration.courseId),
|
|
54328
|
-
...enrollment ? {
|
|
54334
|
+
...enrollment ? { id: enrollment.sourcedId } : {}
|
|
54329
54335
|
};
|
|
54330
54336
|
});
|
|
54331
54337
|
}
|
|
@@ -56904,6 +56910,7 @@ var init_timeback_service = __esm(() => {
|
|
|
56904
56910
|
sessionTimingData,
|
|
56905
56911
|
xpEarned,
|
|
56906
56912
|
masteredUnits,
|
|
56913
|
+
masteredUnitsAbsolute,
|
|
56907
56914
|
extensions,
|
|
56908
56915
|
user
|
|
56909
56916
|
}) {
|
|
@@ -56927,6 +56934,7 @@ var init_timeback_service = __esm(() => {
|
|
|
56927
56934
|
durationSeconds: timingData.durationSeconds,
|
|
56928
56935
|
xpEarned,
|
|
56929
56936
|
masteredUnits,
|
|
56937
|
+
masteredUnitsAbsolute,
|
|
56930
56938
|
extensions: extensionsWithResumeId,
|
|
56931
56939
|
activityId: activityData.activityId,
|
|
56932
56940
|
activityName: activityData.activityName,
|
|
@@ -57175,6 +57183,46 @@ var init_timeback_service = __esm(() => {
|
|
|
57175
57183
|
});
|
|
57176
57184
|
return result;
|
|
57177
57185
|
}
|
|
57186
|
+
async getStudentMastery(timebackId, user, options) {
|
|
57187
|
+
const client = this.requireClient();
|
|
57188
|
+
const db2 = this.deps.db;
|
|
57189
|
+
await this.deps.validateDeveloperAccess(user, options.gameId);
|
|
57190
|
+
const conditions2 = [eq(gameTimebackIntegrations.gameId, options.gameId)];
|
|
57191
|
+
if (options.grade !== undefined && options.subject) {
|
|
57192
|
+
conditions2.push(eq(gameTimebackIntegrations.grade, options.grade));
|
|
57193
|
+
conditions2.push(eq(gameTimebackIntegrations.subject, options.subject));
|
|
57194
|
+
}
|
|
57195
|
+
const integrations = await db2.query.gameTimebackIntegrations.findMany({
|
|
57196
|
+
where: and(...conditions2)
|
|
57197
|
+
});
|
|
57198
|
+
const courseIds = integrations.map((i2) => i2.courseId);
|
|
57199
|
+
if (courseIds.length === 0) {
|
|
57200
|
+
logger20.debug("No integrations found for game, returning empty mastery", {
|
|
57201
|
+
timebackId,
|
|
57202
|
+
gameId: options.gameId,
|
|
57203
|
+
grade: options.grade,
|
|
57204
|
+
subject: options.subject
|
|
57205
|
+
});
|
|
57206
|
+
return {
|
|
57207
|
+
totalMasteredUnits: 0,
|
|
57208
|
+
totalMasterableUnits: 0,
|
|
57209
|
+
...options.include?.perCourse && { courses: [] }
|
|
57210
|
+
};
|
|
57211
|
+
}
|
|
57212
|
+
const result = await client.getStudentMastery(timebackId, {
|
|
57213
|
+
courseIds,
|
|
57214
|
+
include: options.include
|
|
57215
|
+
});
|
|
57216
|
+
logger20.debug("Retrieved student mastery", {
|
|
57217
|
+
timebackId,
|
|
57218
|
+
gameId: options.gameId,
|
|
57219
|
+
grade: options.grade,
|
|
57220
|
+
subject: options.subject,
|
|
57221
|
+
totalMasteredUnits: result.totalMasteredUnits,
|
|
57222
|
+
courseCount: result.courses?.length
|
|
57223
|
+
});
|
|
57224
|
+
return result;
|
|
57225
|
+
}
|
|
57178
57226
|
};
|
|
57179
57227
|
});
|
|
57180
57228
|
|
|
@@ -59481,15 +59529,18 @@ class MasteryTracker {
|
|
|
59481
59529
|
this.edubridgeNamespace = edubridgeNamespace;
|
|
59482
59530
|
}
|
|
59483
59531
|
async checkProgress(input) {
|
|
59484
|
-
const { studentId, courseId, resourceId, masteredUnits } = input;
|
|
59485
|
-
|
|
59532
|
+
const { studentId, courseId, resourceId, masteredUnits, masteredUnitsAbsolute } = input;
|
|
59533
|
+
const hasIncremental = typeof masteredUnits === "number" && masteredUnits !== 0;
|
|
59534
|
+
const hasAbsolute = typeof masteredUnitsAbsolute === "number";
|
|
59535
|
+
if (!hasIncremental && !hasAbsolute) {
|
|
59486
59536
|
return;
|
|
59487
59537
|
}
|
|
59488
59538
|
const status = await this.calculateStatus({
|
|
59489
59539
|
studentId,
|
|
59490
59540
|
courseId,
|
|
59491
59541
|
resourceId,
|
|
59492
|
-
additionalMasteredUnits: masteredUnits
|
|
59542
|
+
additionalMasteredUnits: hasAbsolute ? 0 : masteredUnits,
|
|
59543
|
+
absoluteMasteredUnits: hasAbsolute ? masteredUnitsAbsolute : undefined
|
|
59493
59544
|
});
|
|
59494
59545
|
if (!status) {
|
|
59495
59546
|
return;
|
|
@@ -59498,7 +59549,8 @@ class MasteryTracker {
|
|
|
59498
59549
|
return {
|
|
59499
59550
|
pctCompleteApp: status.pctCompleteApp,
|
|
59500
59551
|
masteryAchieved: !wasComplete && status.isComplete,
|
|
59501
|
-
masteryRevoked: wasComplete && !status.isComplete
|
|
59552
|
+
masteryRevoked: wasComplete && !status.isComplete,
|
|
59553
|
+
effectiveDelta: status.effectiveDelta
|
|
59502
59554
|
};
|
|
59503
59555
|
}
|
|
59504
59556
|
async getStatus(input) {
|
|
@@ -59520,7 +59572,8 @@ class MasteryTracker {
|
|
|
59520
59572
|
studentId,
|
|
59521
59573
|
courseId,
|
|
59522
59574
|
resourceId,
|
|
59523
|
-
additionalMasteredUnits
|
|
59575
|
+
additionalMasteredUnits,
|
|
59576
|
+
absoluteMasteredUnits
|
|
59524
59577
|
}) {
|
|
59525
59578
|
const masterableUnits = await this.resolveMasterableUnits(resourceId);
|
|
59526
59579
|
if (!masterableUnits || masterableUnits <= 0) {
|
|
@@ -59539,7 +59592,15 @@ class MasteryTracker {
|
|
|
59539
59592
|
return;
|
|
59540
59593
|
}
|
|
59541
59594
|
const historicalMasteredUnits = this.sumAnalyticsMetric(facts, "masteredUnits");
|
|
59542
|
-
|
|
59595
|
+
let totalMastered;
|
|
59596
|
+
let effectiveDelta;
|
|
59597
|
+
if (absoluteMasteredUnits !== undefined) {
|
|
59598
|
+
totalMastered = Math.max(0, absoluteMasteredUnits);
|
|
59599
|
+
effectiveDelta = totalMastered - historicalMasteredUnits;
|
|
59600
|
+
} else {
|
|
59601
|
+
effectiveDelta = additionalMasteredUnits;
|
|
59602
|
+
totalMastered = Math.max(0, historicalMasteredUnits + effectiveDelta);
|
|
59603
|
+
}
|
|
59543
59604
|
const rawPct = totalMastered / masterableUnits * 100;
|
|
59544
59605
|
const pctCompleteApp = Math.min(100, Math.max(0, Math.round(rawPct)));
|
|
59545
59606
|
return {
|
|
@@ -59547,7 +59608,8 @@ class MasteryTracker {
|
|
|
59547
59608
|
masterableUnits,
|
|
59548
59609
|
pctCompleteApp,
|
|
59549
59610
|
isComplete: totalMastered >= masterableUnits,
|
|
59550
|
-
historicalMasteredUnits
|
|
59611
|
+
historicalMasteredUnits,
|
|
59612
|
+
effectiveDelta
|
|
59551
59613
|
};
|
|
59552
59614
|
}
|
|
59553
59615
|
async createCompletionEntry(studentId, courseId, classId, appName) {
|
|
@@ -59776,7 +59838,7 @@ class ProgressRecorder {
|
|
|
59776
59838
|
validateProgressData(progressData);
|
|
59777
59839
|
const { ids, activityId, activityName, courseName, student } = await this.resolveContext(courseId, studentIdentifier, progressData);
|
|
59778
59840
|
const { id: studentId, email: studentEmail } = student;
|
|
59779
|
-
const { score, totalQuestions, correctQuestions, xpEarned,
|
|
59841
|
+
const { score, totalQuestions, correctQuestions, xpEarned, attemptNumber } = progressData;
|
|
59780
59842
|
const actualLineItemId = await this.resolveAssessmentLineItem(activityId, activityName, progressData.classId, ids);
|
|
59781
59843
|
const currentAttemptNumber = await this.resolveAttemptNumber(attemptNumber, score, studentId, actualLineItemId);
|
|
59782
59844
|
const calculatedXp = this.calculateXpForProgress(progressData, totalQuestions, correctQuestions, xpEarned, currentAttemptNumber);
|
|
@@ -59785,8 +59847,10 @@ class ProgressRecorder {
|
|
|
59785
59847
|
studentId,
|
|
59786
59848
|
courseId,
|
|
59787
59849
|
resourceId: ids.resource,
|
|
59788
|
-
masteredUnits: progressData.masteredUnits ?? 0
|
|
59850
|
+
masteredUnits: progressData.masteredUnits ?? 0,
|
|
59851
|
+
masteredUnitsAbsolute: progressData.masteredUnitsAbsolute
|
|
59789
59852
|
});
|
|
59853
|
+
const effectiveMasteredUnits = masteryProgress ? masteryProgress.effectiveDelta : progressData.masteredUnits ?? 0;
|
|
59790
59854
|
let pctCompleteApp;
|
|
59791
59855
|
let masteryAchieved = false;
|
|
59792
59856
|
let scoreStatus = SCORE_STATUS5.fullyGraded;
|
|
@@ -59814,7 +59878,7 @@ class ProgressRecorder {
|
|
|
59814
59878
|
appName: progressData.appName,
|
|
59815
59879
|
totalQuestions,
|
|
59816
59880
|
correctQuestions,
|
|
59817
|
-
masteredUnits,
|
|
59881
|
+
masteredUnits: effectiveMasteredUnits || undefined,
|
|
59818
59882
|
pctCompleteApp
|
|
59819
59883
|
});
|
|
59820
59884
|
} else {
|
|
@@ -59852,7 +59916,7 @@ class ProgressRecorder {
|
|
|
59852
59916
|
totalQuestions,
|
|
59853
59917
|
correctQuestions,
|
|
59854
59918
|
xpEarned: calculatedXp,
|
|
59855
|
-
masteredUnits,
|
|
59919
|
+
masteredUnits: effectiveMasteredUnits || undefined,
|
|
59856
59920
|
attemptNumber: currentAttemptNumber,
|
|
59857
59921
|
progressData,
|
|
59858
59922
|
extensions,
|
|
@@ -59861,7 +59925,7 @@ class ProgressRecorder {
|
|
|
59861
59925
|
return {
|
|
59862
59926
|
xpAwarded: calculatedXp,
|
|
59863
59927
|
attemptNumber: currentAttemptNumber,
|
|
59864
|
-
masteredUnitsApplied:
|
|
59928
|
+
masteredUnitsApplied: effectiveMasteredUnits,
|
|
59865
59929
|
pctCompleteApp,
|
|
59866
59930
|
scoreStatus,
|
|
59867
59931
|
inProgress
|
|
@@ -60396,6 +60460,65 @@ class TimebackClient {
|
|
|
60396
60460
|
resourceId: ids.resource
|
|
60397
60461
|
});
|
|
60398
60462
|
}
|
|
60463
|
+
async getStudentMastery(studentId, options) {
|
|
60464
|
+
await this._ensureAuthenticated();
|
|
60465
|
+
const enrollments = await this.edubridge.enrollments.listByUser(studentId);
|
|
60466
|
+
const filteredEnrollments = options?.courseIds?.length ? enrollments.filter((e) => options.courseIds.includes(e.course.id)) : enrollments;
|
|
60467
|
+
if (filteredEnrollments.length === 0) {
|
|
60468
|
+
return {
|
|
60469
|
+
totalMasteredUnits: 0,
|
|
60470
|
+
totalMasterableUnits: 0,
|
|
60471
|
+
...options?.include?.perCourse && { courses: [] }
|
|
60472
|
+
};
|
|
60473
|
+
}
|
|
60474
|
+
const masteryResults = await Promise.all(filteredEnrollments.map(async (enrollment) => {
|
|
60475
|
+
try {
|
|
60476
|
+
const ids = deriveSourcedIds2(enrollment.course.id);
|
|
60477
|
+
const status = await this.masteryTracker.getStatus({
|
|
60478
|
+
studentId,
|
|
60479
|
+
courseId: enrollment.course.id,
|
|
60480
|
+
resourceId: ids.resource
|
|
60481
|
+
});
|
|
60482
|
+
return { enrollment, status };
|
|
60483
|
+
} catch (error) {
|
|
60484
|
+
log.warn("[TimebackClient] Failed to fetch mastery for enrollment", {
|
|
60485
|
+
enrollmentId: enrollment.id,
|
|
60486
|
+
error
|
|
60487
|
+
});
|
|
60488
|
+
return { enrollment, status: undefined };
|
|
60489
|
+
}
|
|
60490
|
+
}));
|
|
60491
|
+
let totalMasteredUnits = 0;
|
|
60492
|
+
let totalMasterableUnits = 0;
|
|
60493
|
+
const courses = [];
|
|
60494
|
+
for (const { enrollment, status } of masteryResults) {
|
|
60495
|
+
const masteredUnits = status?.masteredUnits ?? 0;
|
|
60496
|
+
const masterableUnits = status?.masterableUnits ?? 0;
|
|
60497
|
+
totalMasteredUnits += masteredUnits;
|
|
60498
|
+
totalMasterableUnits += masterableUnits;
|
|
60499
|
+
if (options?.include?.perCourse) {
|
|
60500
|
+
const gradeStr = enrollment.course.grades?.[0];
|
|
60501
|
+
const parsedGrade = gradeStr ? parseInt(gradeStr, 10) : 0;
|
|
60502
|
+
const grade = isTimebackGrade3(parsedGrade) ? parsedGrade : 0;
|
|
60503
|
+
const subjectStr = enrollment.course.subjects?.[0];
|
|
60504
|
+
const subject = subjectStr && isTimebackSubject3(subjectStr) ? subjectStr : "None";
|
|
60505
|
+
courses.push({
|
|
60506
|
+
grade,
|
|
60507
|
+
subject,
|
|
60508
|
+
title: enrollment.course.title,
|
|
60509
|
+
masteredUnits,
|
|
60510
|
+
masterableUnits,
|
|
60511
|
+
pctComplete: status?.pctCompleteApp ?? 0,
|
|
60512
|
+
isComplete: status?.isComplete ?? false
|
|
60513
|
+
});
|
|
60514
|
+
}
|
|
60515
|
+
}
|
|
60516
|
+
return {
|
|
60517
|
+
totalMasteredUnits,
|
|
60518
|
+
totalMasterableUnits,
|
|
60519
|
+
...options?.include?.perCourse && { courses }
|
|
60520
|
+
};
|
|
60521
|
+
}
|
|
60399
60522
|
async getStudentXp(studentId, options) {
|
|
60400
60523
|
await this._ensureAuthenticated();
|
|
60401
60524
|
const enrollments = await this.edubridge.enrollments.listByUser(studentId);
|
|
@@ -120401,6 +120524,7 @@ var endActivity;
|
|
|
120401
120524
|
var heartbeat;
|
|
120402
120525
|
var advanceCourse;
|
|
120403
120526
|
var getStudentXp;
|
|
120527
|
+
var getStudentMastery;
|
|
120404
120528
|
var getRoster;
|
|
120405
120529
|
var getStudentOverview;
|
|
120406
120530
|
var getGameMetrics;
|
|
@@ -120585,6 +120709,7 @@ var init_timeback_controller = __esm(() => {
|
|
|
120585
120709
|
sessionTimingData,
|
|
120586
120710
|
xpEarned,
|
|
120587
120711
|
masteredUnits,
|
|
120712
|
+
masteredUnitsAbsolute,
|
|
120588
120713
|
extensions
|
|
120589
120714
|
} = body2;
|
|
120590
120715
|
logger45.debug("Ending activity", { userId: ctx.user.id, gameId });
|
|
@@ -120599,6 +120724,7 @@ var init_timeback_controller = __esm(() => {
|
|
|
120599
120724
|
sessionTimingData,
|
|
120600
120725
|
xpEarned,
|
|
120601
120726
|
masteredUnits,
|
|
120727
|
+
masteredUnitsAbsolute,
|
|
120602
120728
|
extensions,
|
|
120603
120729
|
user: ctx.user
|
|
120604
120730
|
});
|
|
@@ -120708,6 +120834,53 @@ var init_timeback_controller = __esm(() => {
|
|
|
120708
120834
|
include
|
|
120709
120835
|
});
|
|
120710
120836
|
});
|
|
120837
|
+
getStudentMastery = requireDeveloper(async (ctx) => {
|
|
120838
|
+
const timebackId = ctx.params.timebackId;
|
|
120839
|
+
if (!timebackId) {
|
|
120840
|
+
throw ApiError.badRequest("Missing timebackId parameter");
|
|
120841
|
+
}
|
|
120842
|
+
const gameId = ctx.url.searchParams.get("gameId");
|
|
120843
|
+
if (!gameId) {
|
|
120844
|
+
throw ApiError.badRequest("Missing required gameId query parameter");
|
|
120845
|
+
}
|
|
120846
|
+
const gradeParam = ctx.url.searchParams.get("grade");
|
|
120847
|
+
const subjectParam = ctx.url.searchParams.get("subject");
|
|
120848
|
+
if (gradeParam !== null !== (subjectParam !== null)) {
|
|
120849
|
+
throw ApiError.badRequest("Both grade and subject must be provided together");
|
|
120850
|
+
}
|
|
120851
|
+
let grade;
|
|
120852
|
+
let subject;
|
|
120853
|
+
if (gradeParam !== null && subjectParam !== null) {
|
|
120854
|
+
const parsedGrade = parseInt(gradeParam, 10);
|
|
120855
|
+
if (!isTimebackGrade2(parsedGrade)) {
|
|
120856
|
+
throw ApiError.badRequest(`Invalid grade: ${gradeParam}. Valid grades: ${TIMEBACK_GRADES.join(", ")}`);
|
|
120857
|
+
}
|
|
120858
|
+
if (!isTimebackSubject2(subjectParam)) {
|
|
120859
|
+
throw ApiError.badRequest(`Invalid subject: ${subjectParam}. Valid subjects: ${TIMEBACK_SUBJECTS4.join(", ")}`);
|
|
120860
|
+
}
|
|
120861
|
+
grade = parsedGrade;
|
|
120862
|
+
subject = subjectParam;
|
|
120863
|
+
}
|
|
120864
|
+
const includeParam = ctx.url.searchParams.get("include");
|
|
120865
|
+
const includeOptions = includeParam ? includeParam.split(",").map((opt) => opt.trim().toLowerCase()) : [];
|
|
120866
|
+
const include = {
|
|
120867
|
+
perCourse: includeOptions.includes("percourse")
|
|
120868
|
+
};
|
|
120869
|
+
logger45.debug("Getting student mastery", {
|
|
120870
|
+
requesterId: ctx.user.id,
|
|
120871
|
+
timebackId,
|
|
120872
|
+
gameId,
|
|
120873
|
+
grade,
|
|
120874
|
+
subject,
|
|
120875
|
+
include
|
|
120876
|
+
});
|
|
120877
|
+
return ctx.services.timeback.getStudentMastery(timebackId, ctx.user, {
|
|
120878
|
+
gameId,
|
|
120879
|
+
grade,
|
|
120880
|
+
subject,
|
|
120881
|
+
include
|
|
120882
|
+
});
|
|
120883
|
+
});
|
|
120711
120884
|
getRoster = requireGameManagementAccess(async (ctx) => {
|
|
120712
120885
|
const gameId = ctx.params.gameId;
|
|
120713
120886
|
const courseId = ctx.params.courseId;
|
|
@@ -121040,6 +121213,7 @@ var init_timeback_controller = __esm(() => {
|
|
|
121040
121213
|
heartbeat,
|
|
121041
121214
|
advanceCourse,
|
|
121042
121215
|
getStudentXp,
|
|
121216
|
+
getStudentMastery,
|
|
121043
121217
|
getRoster,
|
|
121044
121218
|
getStudentOverview,
|
|
121045
121219
|
getGameMetrics,
|
|
@@ -121890,6 +122064,7 @@ var init_timeback6 = __esm(() => {
|
|
|
121890
122064
|
init_controllers();
|
|
121891
122065
|
init_errors();
|
|
121892
122066
|
init_utils11();
|
|
122067
|
+
init_schemas_index();
|
|
121893
122068
|
init_api();
|
|
121894
122069
|
init_error_handler();
|
|
121895
122070
|
init_timeback5();
|
|
@@ -121969,6 +122144,14 @@ var init_timeback6 = __esm(() => {
|
|
|
121969
122144
|
}
|
|
121970
122145
|
if (gradeParam !== null && subjectParam !== null) {
|
|
121971
122146
|
const grade = parseInt(gradeParam, 10);
|
|
122147
|
+
if (!Number.isFinite(grade) || !isTimebackGrade2(grade)) {
|
|
122148
|
+
const error2 = ApiError.badRequest(`Invalid grade: ${gradeParam}. Valid grades: ${TIMEBACK_GRADES.join(", ")}`);
|
|
122149
|
+
return c2.json(createErrorResponse(error2), error2.status);
|
|
122150
|
+
}
|
|
122151
|
+
if (!isTimebackSubject2(subjectParam)) {
|
|
122152
|
+
const error2 = ApiError.badRequest(`Invalid subject: ${subjectParam}. Valid subjects: ${TIMEBACK_SUBJECTS4.join(", ")}`);
|
|
122153
|
+
return c2.json(createErrorResponse(error2), error2.status);
|
|
122154
|
+
}
|
|
121972
122155
|
enrollments = enrollments.filter((e) => e.grade === grade && e.subject === subjectParam);
|
|
121973
122156
|
}
|
|
121974
122157
|
const mockCourses = enrollments.map((e) => {
|
|
@@ -121993,6 +122176,62 @@ var init_timeback6 = __esm(() => {
|
|
|
121993
122176
|
}
|
|
121994
122177
|
return handle2(timeback2.getStudentXp)(c2);
|
|
121995
122178
|
});
|
|
122179
|
+
timebackRouter.get("/student-mastery/:timebackId", async (c2) => {
|
|
122180
|
+
const user = c2.get("user");
|
|
122181
|
+
if (!user) {
|
|
122182
|
+
const error2 = ApiError.unauthorized("Must be logged in to get student mastery");
|
|
122183
|
+
return c2.json(createErrorResponse(error2), error2.status);
|
|
122184
|
+
}
|
|
122185
|
+
if (shouldMockTimeback()) {
|
|
122186
|
+
const url2 = new URL(c2.req.url);
|
|
122187
|
+
const gradeParam = url2.searchParams.get("grade");
|
|
122188
|
+
const subjectParam = url2.searchParams.get("subject");
|
|
122189
|
+
const includeParam = url2.searchParams.get("include") || "";
|
|
122190
|
+
const includeOptions = includeParam.split(",").map((opt) => opt.trim().toLowerCase());
|
|
122191
|
+
const includePerCourse = includeOptions.includes("percourse");
|
|
122192
|
+
const db2 = c2.get("db");
|
|
122193
|
+
let enrollments = await getMockEnrollments(db2);
|
|
122194
|
+
if (gradeParam !== null !== (subjectParam !== null)) {
|
|
122195
|
+
const error2 = ApiError.badRequest("Both grade and subject must be provided together");
|
|
122196
|
+
return c2.json(createErrorResponse(error2), error2.status);
|
|
122197
|
+
}
|
|
122198
|
+
if (gradeParam !== null && subjectParam !== null) {
|
|
122199
|
+
const grade = parseInt(gradeParam, 10);
|
|
122200
|
+
if (!Number.isFinite(grade) || !isTimebackGrade2(grade)) {
|
|
122201
|
+
const error2 = ApiError.badRequest(`Invalid grade: ${gradeParam}. Valid grades: ${TIMEBACK_GRADES.join(", ")}`);
|
|
122202
|
+
return c2.json(createErrorResponse(error2), error2.status);
|
|
122203
|
+
}
|
|
122204
|
+
if (!isTimebackSubject2(subjectParam)) {
|
|
122205
|
+
const error2 = ApiError.badRequest(`Invalid subject: ${subjectParam}. Valid subjects: ${TIMEBACK_SUBJECTS4.join(", ")}`);
|
|
122206
|
+
return c2.json(createErrorResponse(error2), error2.status);
|
|
122207
|
+
}
|
|
122208
|
+
enrollments = enrollments.filter((e) => e.grade === grade && e.subject === subjectParam);
|
|
122209
|
+
}
|
|
122210
|
+
const mockCourses = enrollments.map((e) => {
|
|
122211
|
+
const seed3 = hashCode(`${e.grade}-${e.subject}`);
|
|
122212
|
+
const masterableUnits = 5 + seed3 % 16;
|
|
122213
|
+
const masteredUnits = seed3 % (masterableUnits + 1);
|
|
122214
|
+
const pctComplete = masterableUnits > 0 ? Math.round(masteredUnits / masterableUnits * 1e4) / 100 : 0;
|
|
122215
|
+
return {
|
|
122216
|
+
grade: e.grade,
|
|
122217
|
+
subject: e.subject,
|
|
122218
|
+
title: `${e.subject} ${formatGradeLabel(e.grade)}`,
|
|
122219
|
+
masteredUnits,
|
|
122220
|
+
masterableUnits,
|
|
122221
|
+
pctComplete,
|
|
122222
|
+
isComplete: masteredUnits >= masterableUnits
|
|
122223
|
+
};
|
|
122224
|
+
});
|
|
122225
|
+
const totalMasteredUnits = mockCourses.reduce((sum2, course) => sum2 + course.masteredUnits, 0);
|
|
122226
|
+
const totalMasterableUnits = mockCourses.reduce((sum2, course) => sum2 + course.masterableUnits, 0);
|
|
122227
|
+
return c2.json({
|
|
122228
|
+
totalMasteredUnits,
|
|
122229
|
+
totalMasterableUnits,
|
|
122230
|
+
...includePerCourse && { courses: mockCourses }
|
|
122231
|
+
});
|
|
122232
|
+
}
|
|
122233
|
+
return handle2(timeback2.getStudentMastery)(c2);
|
|
122234
|
+
});
|
|
121996
122235
|
});
|
|
121997
122236
|
function verifyMockToken(idToken) {
|
|
121998
122237
|
if (!idToken.startsWith("mock:")) {
|
|
@@ -122748,6 +122987,7 @@ var init_timeback7 = __esm8(() => {
|
|
|
122748
122987
|
TIMEBACK_ROUTES2 = {
|
|
122749
122988
|
END_ACTIVITY: "/integrations/timeback/end-activity",
|
|
122750
122989
|
GET_XP: "/integrations/timeback/xp",
|
|
122990
|
+
GET_MASTERY: "/integrations/timeback/mastery",
|
|
122751
122991
|
HEARTBEAT: "/integrations/timeback/heartbeat",
|
|
122752
122992
|
ADVANCE_COURSE: "/integrations/timeback/advance-course"
|
|
122753
122993
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playcademy/vite-plugin",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3-beta.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"archiver": "^7.0.1",
|
|
21
21
|
"picocolors": "^1.1.1",
|
|
22
|
-
"playcademy": "0.22.
|
|
22
|
+
"playcademy": "0.22.1"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@electric-sql/pglite": "^0.3.16",
|
|
26
26
|
"@inquirer/prompts": "^7.8.6",
|
|
27
27
|
"@playcademy/constants": "0.0.1",
|
|
28
|
-
"@playcademy/sandbox": "0.
|
|
29
|
-
"@playcademy/sdk": "0.
|
|
28
|
+
"@playcademy/sandbox": "0.4.0",
|
|
29
|
+
"@playcademy/sdk": "0.10.0",
|
|
30
30
|
"@playcademy/types": "0.0.1",
|
|
31
31
|
"@playcademy/utils": "0.0.1",
|
|
32
32
|
"@types/archiver": "^6.0.3",
|