@playcademy/sandbox 0.3.17-beta.20 → 0.3.17-beta.21

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 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.20",
1333
+ version: "0.3.17-beta.21",
1334
1334
  description: "Local development server for Playcademy game development",
1335
1335
  type: "module",
1336
1336
  exports: {
@@ -36277,7 +36277,7 @@ class MasteryTracker {
36277
36277
  }
36278
36278
  async checkProgress(input) {
36279
36279
  const { studentId, courseId, resourceId, masteredUnits } = input;
36280
- if (typeof masteredUnits !== "number" || masteredUnits <= 0) {
36280
+ if (typeof masteredUnits !== "number" || masteredUnits === 0) {
36281
36281
  return;
36282
36282
  }
36283
36283
  const status = await this.calculateStatus({
@@ -36289,9 +36289,11 @@ class MasteryTracker {
36289
36289
  if (!status) {
36290
36290
  return;
36291
36291
  }
36292
+ const wasComplete = status.historicalMasteredUnits >= status.masterableUnits;
36292
36293
  return {
36293
36294
  pctCompleteApp: status.pctCompleteApp,
36294
- masteryAchieved: status.historicalMasteredUnits < status.masterableUnits && status.isComplete
36295
+ masteryAchieved: !wasComplete && status.isComplete,
36296
+ masteryRevoked: wasComplete && !status.isComplete
36295
36297
  };
36296
36298
  }
36297
36299
  async getStatus(input) {
@@ -36332,7 +36334,7 @@ class MasteryTracker {
36332
36334
  return;
36333
36335
  }
36334
36336
  const historicalMasteredUnits = this.sumAnalyticsMetric(facts, "masteredUnits");
36335
- const totalMastered = historicalMasteredUnits + additionalMasteredUnits;
36337
+ const totalMastered = Math.max(0, historicalMasteredUnits + additionalMasteredUnits);
36336
36338
  const rawPct = totalMastered / masterableUnits * 100;
36337
36339
  const pctCompleteApp = Math.min(100, Math.max(0, Math.round(rawPct)));
36338
36340
  return {
@@ -36382,6 +36384,45 @@ class MasteryTracker {
36382
36384
  });
36383
36385
  }
36384
36386
  }
36387
+ async revokeCompletionEntry(studentId, courseId, classId, appName) {
36388
+ const ids = deriveSourcedIds2(courseId);
36389
+ const lineItemId = `${ids.course}-mastery-completion-assessment`;
36390
+ const resultId = `${lineItemId}:${studentId}:completion`;
36391
+ try {
36392
+ await this.onerosterNamespace.assessmentLineItems.findOrCreate(lineItemId, {
36393
+ sourcedId: lineItemId,
36394
+ title: "Mastery Completion",
36395
+ status: ONEROSTER_STATUS4.active,
36396
+ ...classId ? { class: { sourcedId: classId } } : { course: { sourcedId: ids.course } },
36397
+ ...ids.componentResource ? { componentResource: { sourcedId: ids.componentResource } } : {}
36398
+ });
36399
+ await this.onerosterNamespace.assessmentResults.upsert(resultId, {
36400
+ sourcedId: resultId,
36401
+ status: ONEROSTER_STATUS4.active,
36402
+ assessmentLineItem: { sourcedId: lineItemId },
36403
+ student: { sourcedId: studentId },
36404
+ score: 0,
36405
+ scoreDate: new Date().toISOString(),
36406
+ scoreStatus: SCORE_STATUS4.notSubmitted,
36407
+ inProgress: "true",
36408
+ metadata: {
36409
+ isMasteryCompletion: true,
36410
+ appName
36411
+ }
36412
+ });
36413
+ log.info("[MasteryTracker] Revoked mastery completion entry", {
36414
+ studentId,
36415
+ lineItemId,
36416
+ resultId
36417
+ });
36418
+ } catch (error) {
36419
+ log.error("[MasteryTracker] Failed to revoke mastery completion entry", {
36420
+ studentId,
36421
+ lineItemId,
36422
+ error
36423
+ });
36424
+ }
36425
+ }
36385
36426
  async resolveMasterableUnits(resourceId) {
36386
36427
  if (!resourceId) {
36387
36428
  return;
@@ -36591,6 +36632,9 @@ class ProgressRecorder {
36591
36632
  sensorUrl: progressData.sensorUrl
36592
36633
  });
36593
36634
  }
36635
+ if (masteryProgress?.masteryRevoked) {
36636
+ await this.masteryTracker.revokeCompletionEntry(studentId, courseId, progressData.classId, progressData.appName);
36637
+ }
36594
36638
  await this.emitCaliperEvent({
36595
36639
  studentId,
36596
36640
  studentEmail,
@@ -94183,7 +94227,7 @@ var init_schemas11 = __esm(() => {
94183
94227
  inactiveSeconds: exports_external.number().nonnegative().optional()
94184
94228
  }).optional(),
94185
94229
  xpEarned: exports_external.number().optional(),
94186
- masteredUnits: exports_external.number().nonnegative().optional(),
94230
+ masteredUnits: exports_external.number().optional(),
94187
94231
  extensions: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
94188
94232
  });
94189
94233
  AdvanceCourseRequestSchema = exports_external.object({
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.20",
1332
+ version: "0.3.17-beta.21",
1333
1333
  description: "Local development server for Playcademy game development",
1334
1334
  type: "module",
1335
1335
  exports: {
@@ -36276,7 +36276,7 @@ class MasteryTracker {
36276
36276
  }
36277
36277
  async checkProgress(input) {
36278
36278
  const { studentId, courseId, resourceId, masteredUnits } = input;
36279
- if (typeof masteredUnits !== "number" || masteredUnits <= 0) {
36279
+ if (typeof masteredUnits !== "number" || masteredUnits === 0) {
36280
36280
  return;
36281
36281
  }
36282
36282
  const status = await this.calculateStatus({
@@ -36288,9 +36288,11 @@ class MasteryTracker {
36288
36288
  if (!status) {
36289
36289
  return;
36290
36290
  }
36291
+ const wasComplete = status.historicalMasteredUnits >= status.masterableUnits;
36291
36292
  return {
36292
36293
  pctCompleteApp: status.pctCompleteApp,
36293
- masteryAchieved: status.historicalMasteredUnits < status.masterableUnits && status.isComplete
36294
+ masteryAchieved: !wasComplete && status.isComplete,
36295
+ masteryRevoked: wasComplete && !status.isComplete
36294
36296
  };
36295
36297
  }
36296
36298
  async getStatus(input) {
@@ -36331,7 +36333,7 @@ class MasteryTracker {
36331
36333
  return;
36332
36334
  }
36333
36335
  const historicalMasteredUnits = this.sumAnalyticsMetric(facts, "masteredUnits");
36334
- const totalMastered = historicalMasteredUnits + additionalMasteredUnits;
36336
+ const totalMastered = Math.max(0, historicalMasteredUnits + additionalMasteredUnits);
36335
36337
  const rawPct = totalMastered / masterableUnits * 100;
36336
36338
  const pctCompleteApp = Math.min(100, Math.max(0, Math.round(rawPct)));
36337
36339
  return {
@@ -36381,6 +36383,45 @@ class MasteryTracker {
36381
36383
  });
36382
36384
  }
36383
36385
  }
36386
+ async revokeCompletionEntry(studentId, courseId, classId, appName) {
36387
+ const ids = deriveSourcedIds2(courseId);
36388
+ const lineItemId = `${ids.course}-mastery-completion-assessment`;
36389
+ const resultId = `${lineItemId}:${studentId}:completion`;
36390
+ try {
36391
+ await this.onerosterNamespace.assessmentLineItems.findOrCreate(lineItemId, {
36392
+ sourcedId: lineItemId,
36393
+ title: "Mastery Completion",
36394
+ status: ONEROSTER_STATUS4.active,
36395
+ ...classId ? { class: { sourcedId: classId } } : { course: { sourcedId: ids.course } },
36396
+ ...ids.componentResource ? { componentResource: { sourcedId: ids.componentResource } } : {}
36397
+ });
36398
+ await this.onerosterNamespace.assessmentResults.upsert(resultId, {
36399
+ sourcedId: resultId,
36400
+ status: ONEROSTER_STATUS4.active,
36401
+ assessmentLineItem: { sourcedId: lineItemId },
36402
+ student: { sourcedId: studentId },
36403
+ score: 0,
36404
+ scoreDate: new Date().toISOString(),
36405
+ scoreStatus: SCORE_STATUS4.notSubmitted,
36406
+ inProgress: "true",
36407
+ metadata: {
36408
+ isMasteryCompletion: true,
36409
+ appName
36410
+ }
36411
+ });
36412
+ log.info("[MasteryTracker] Revoked mastery completion entry", {
36413
+ studentId,
36414
+ lineItemId,
36415
+ resultId
36416
+ });
36417
+ } catch (error) {
36418
+ log.error("[MasteryTracker] Failed to revoke mastery completion entry", {
36419
+ studentId,
36420
+ lineItemId,
36421
+ error
36422
+ });
36423
+ }
36424
+ }
36384
36425
  async resolveMasterableUnits(resourceId) {
36385
36426
  if (!resourceId) {
36386
36427
  return;
@@ -36590,6 +36631,9 @@ class ProgressRecorder {
36590
36631
  sensorUrl: progressData.sensorUrl
36591
36632
  });
36592
36633
  }
36634
+ if (masteryProgress?.masteryRevoked) {
36635
+ await this.masteryTracker.revokeCompletionEntry(studentId, courseId, progressData.classId, progressData.appName);
36636
+ }
36593
36637
  await this.emitCaliperEvent({
36594
36638
  studentId,
36595
36639
  studentEmail,
@@ -94182,7 +94226,7 @@ var init_schemas11 = __esm(() => {
94182
94226
  inactiveSeconds: exports_external.number().nonnegative().optional()
94183
94227
  }).optional(),
94184
94228
  xpEarned: exports_external.number().optional(),
94185
- masteredUnits: exports_external.number().nonnegative().optional(),
94229
+ masteredUnits: exports_external.number().optional(),
94186
94230
  extensions: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
94187
94231
  });
94188
94232
  AdvanceCourseRequestSchema = exports_external.object({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playcademy/sandbox",
3
- "version": "0.3.17-beta.20",
3
+ "version": "0.3.17-beta.21",
4
4
  "description": "Local development server for Playcademy game development",
5
5
  "type": "module",
6
6
  "exports": {