@playcademy/vite-plugin 0.2.26 → 0.2.27-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.
Files changed (2) hide show
  1. package/dist/index.js +85 -20
  2. 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.16",
25373
+ version: "0.3.17-beta.24",
25374
25374
  description: "Local development server for Playcademy game development",
25375
25375
  type: "module",
25376
25376
  exports: {
@@ -54793,13 +54793,54 @@ class TimebackAdminService {
54793
54793
  const remediationItems = events.map((event) => mapCaliperEventToRemediationActivity(event, relevantCourseIds)).filter((item) => Boolean(item));
54794
54794
  return [...groupedGameplayItems, ...remediationItems].toSorted((a, b) => b.occurredAt.localeCompare(a.occurredAt));
54795
54795
  }
54796
- async getStudentEnrollmentsByCourseId(client, studentId, courseIds) {
54797
- const relevantCourseIds = new Set(courseIds);
54798
- const enrollments = await client.edubridge.enrollments.listByUser(studentId);
54799
- return new Map(enrollments.filter((enrollment) => relevantCourseIds.has(enrollment.course.id)).map((enrollment) => [enrollment.course.id, enrollment]));
54796
+ async getStudentEnrollmentsByCourseId(client, studentId, courseIds, options) {
54797
+ const enrollments = new Map;
54798
+ const entries = await Promise.all(courseIds.map(async (courseId) => {
54799
+ const roster = await client.oneroster.enrollments.listByCourse(courseId, {
54800
+ includeInactive: options?.includeInactive,
54801
+ includeUsers: false
54802
+ });
54803
+ const matches = roster.filter((entry) => entry.enrollment.user.sourcedId === studentId).toSorted((a, b) => {
54804
+ const aActive = a.enrollment.status === "active";
54805
+ const bActive = b.enrollment.status === "active";
54806
+ if (aActive !== bActive) {
54807
+ return aActive ? -1 : 1;
54808
+ }
54809
+ return (b.enrollment.dateLastModified ?? "").localeCompare(a.enrollment.dateLastModified ?? "");
54810
+ });
54811
+ return { courseId, match: matches[0] ?? null };
54812
+ }));
54813
+ for (const { courseId, match } of entries) {
54814
+ if (match) {
54815
+ enrollments.set(courseId, {
54816
+ id: match.enrollment.sourcedId,
54817
+ status: match.enrollment.status ?? "active",
54818
+ role: match.enrollment.role ?? "student",
54819
+ beginDate: match.enrollment.beginDate ?? null,
54820
+ endDate: match.enrollment.endDate ?? null,
54821
+ course: {
54822
+ id: courseId,
54823
+ title: match.class?.title ?? "",
54824
+ subjects: null,
54825
+ grades: null
54826
+ }
54827
+ });
54828
+ }
54829
+ }
54830
+ return { enrollments };
54800
54831
  }
54801
54832
  async assertStudentEnrolledInCourse(client, studentId, courseId) {
54802
- const enrollmentsByCourseId = await this.getStudentEnrollmentsByCourseId(client, studentId, [courseId]);
54833
+ const enrollments = await client.edubridge.enrollments.listByUser(studentId);
54834
+ const match = enrollments.find((e) => e.course.id === courseId);
54835
+ if (!match) {
54836
+ throw new NotFoundError("Student enrollment", `${studentId}:${courseId}`);
54837
+ }
54838
+ return match;
54839
+ }
54840
+ async assertStudentHasEnrollmentInCourse(client, studentId, courseId) {
54841
+ const { enrollments: enrollmentsByCourseId } = await this.getStudentEnrollmentsByCourseId(client, studentId, [courseId], {
54842
+ includeInactive: true
54843
+ });
54803
54844
  if (!enrollmentsByCourseId.has(courseId)) {
54804
54845
  throw new NotFoundError("Student enrollment", `${studentId}:${courseId}`);
54805
54846
  }
@@ -54852,7 +54893,7 @@ class TimebackAdminService {
54852
54893
  return [];
54853
54894
  }
54854
54895
  }
54855
- async listStudentsForCourse(gameId, courseId, user) {
54896
+ async listStudentsForCourse(gameId, courseId, user, options) {
54856
54897
  const client = this.requireClient();
54857
54898
  await this.deps.validateGameManagementAccess(user, gameId);
54858
54899
  const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
@@ -54862,7 +54903,8 @@ class TimebackAdminService {
54862
54903
  throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
54863
54904
  }
54864
54905
  const roster = await client.oneroster.enrollments.listByCourse(courseId, {
54865
- role: "student"
54906
+ role: "student",
54907
+ includeInactive: options?.includeInactive
54866
54908
  });
54867
54909
  const analyticsByEnrollmentId = await this.loadEnrollmentAnalyticsSummaries(roster.map((rosterEntry) => rosterEntry.enrollment.sourcedId).filter((enrollmentId) => Boolean(enrollmentId)));
54868
54910
  const masterableUnits = await this.getMasterableUnits(courseId);
@@ -54871,6 +54913,7 @@ class TimebackAdminService {
54871
54913
  const summary = enrollmentId ? analyticsByEnrollmentId.get(enrollmentId) : undefined;
54872
54914
  const analyticsUnavailable = Boolean(enrollmentId) && summary?.analyticsAvailable !== true;
54873
54915
  const name3 = rosterEntry.user ? `${rosterEntry.user.givenName} ${rosterEntry.user.familyName}`.trim() : rosterEntry.enrollment.user.sourcedId;
54916
+ const inactive = rosterEntry.enrollment.status === "tobedeleted";
54874
54917
  return {
54875
54918
  studentId: rosterEntry.enrollment.user.sourcedId,
54876
54919
  enrollmentId,
@@ -54884,11 +54927,25 @@ class TimebackAdminService {
54884
54927
  activeTimeSeconds: summary?.activeTimeSeconds ?? 0,
54885
54928
  masteredUnits: summary?.masteredUnits ?? 0,
54886
54929
  masterableUnits,
54887
- pctCompleteApp: TimebackAdminService.computeCompletionPct(summary?.masteredUnits ?? 0, masterableUnits)
54930
+ pctCompleteApp: TimebackAdminService.computeCompletionPct(summary?.masteredUnits ?? 0, masterableUnits),
54931
+ ...inactive ? { inactive } : {}
54888
54932
  };
54889
54933
  });
54890
- students.sort((a, b) => a.name.localeCompare(b.name));
54891
- return { gameId, courseId, students };
54934
+ const studentMap = new Map;
54935
+ for (const student of students) {
54936
+ const existing = studentMap.get(student.studentId);
54937
+ if (!existing || existing.inactive && !student.inactive) {
54938
+ studentMap.set(student.studentId, student);
54939
+ }
54940
+ }
54941
+ const deduped = [...studentMap.values()];
54942
+ deduped.sort((a, b) => {
54943
+ if (a.inactive !== b.inactive) {
54944
+ return a.inactive ? 1 : -1;
54945
+ }
54946
+ return a.name.localeCompare(b.name);
54947
+ });
54948
+ return { gameId, courseId, students: deduped };
54892
54949
  }
54893
54950
  async getStudentOverview(gameId, studentId, user, courseId) {
54894
54951
  const client = this.requireClient();
@@ -54900,7 +54957,9 @@ class TimebackAdminService {
54900
54957
  throw new NotFoundError("Timeback integration", gameId);
54901
54958
  }
54902
54959
  const courseIds = new Set(integrations.map((integration) => integration.courseId));
54903
- const enrollmentsByCourseId = await this.getStudentEnrollmentsByCourseId(client, studentId, [...courseIds]);
54960
+ const { enrollments: enrollmentsByCourseId } = await this.getStudentEnrollmentsByCourseId(client, studentId, [...courseIds], {
54961
+ includeInactive: true
54962
+ });
54904
54963
  if (enrollmentsByCourseId.size === 0) {
54905
54964
  throw new NotFoundError("Student enrollment", courseId ? `${studentId}:${courseId}` : `${studentId}:${gameId}`);
54906
54965
  }
@@ -54915,6 +54974,7 @@ class TimebackAdminService {
54915
54974
  const summary = enrollment ? analyticsByEnrollmentId.get(enrollment.id) : undefined;
54916
54975
  const masterableUnits = masterableUnitsByCourse.get(integration.courseId);
54917
54976
  const analyticsUnavailable = Boolean(enrollment?.id) && summary?.analyticsAvailable !== true;
54977
+ const inactive = enrollment?.status === "tobedeleted";
54918
54978
  return {
54919
54979
  courseId: integration.courseId,
54920
54980
  title: enrollment?.course.title || `${integration.subject} Grade ${integration.grade}`,
@@ -54929,7 +54989,8 @@ class TimebackAdminService {
54929
54989
  masterableUnits,
54930
54990
  pctCompleteApp: TimebackAdminService.computeCompletionPct(summary?.masteredUnits ?? 0, masterableUnits),
54931
54991
  completionStatus: completionStatusByCourse.get(integration.courseId) ?? "none",
54932
- history: summary?.history ?? []
54992
+ history: summary?.history ?? [],
54993
+ ...inactive ? { inactive } : {}
54933
54994
  };
54934
54995
  });
54935
54996
  return {
@@ -54956,7 +55017,7 @@ class TimebackAdminService {
54956
55017
  if (!integration) {
54957
55018
  throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
54958
55019
  }
54959
- await this.assertStudentEnrolledInCourse(client, studentId, courseId);
55020
+ await this.assertStudentHasEnrollmentInCourse(client, studentId, courseId);
54960
55021
  const relevantCourseIds = new Set([courseId]);
54961
55022
  const fetchLimit = Math.min(safeOffset + safeLimit + 1, TimebackAdminService.MAX_STUDENT_ACTIVITY_OFFSET + TimebackAdminService.MAX_STUDENT_ACTIVITY_LIMIT + 1);
54962
55023
  const allActivities = await this.listRecentActivityForStudent(client, studentId, gameSource, relevantCourseIds, fetchLimit);
@@ -54977,7 +55038,7 @@ class TimebackAdminService {
54977
55038
  if (!integration) {
54978
55039
  throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
54979
55040
  }
54980
- await this.assertStudentEnrolledInCourse(client, studentId, courseId);
55041
+ await this.assertStudentHasEnrollmentInCourse(client, studentId, courseId);
54981
55042
  const events = await this.fetchCaliperEventsForStudent(client, studentId, gameSource, TimebackAdminService.MAX_RECENT_ACTIVITY_EVENT_FETCH);
54982
55043
  const relevantCourseIds = new Set([courseId]);
54983
55044
  let matchedEvents;
@@ -59540,7 +59601,7 @@ function createOneRosterNamespace(client) {
59540
59601
  listByClass: async (classSourcedId, options) => {
59541
59602
  const queryParams = new URLSearchParams;
59542
59603
  const filters = [`class.sourcedId='${escapeFilterValue2(classSourcedId)}'`];
59543
- if (options?.onlyActive ?? true) {
59604
+ if (!options?.includeInactive) {
59544
59605
  filters.push(`status='active'`);
59545
59606
  }
59546
59607
  queryParams.set("filter", filters.join(" AND "));
@@ -59575,7 +59636,7 @@ function createOneRosterNamespace(client) {
59575
59636
  const queryParams = new URLSearchParams;
59576
59637
  const classFilter = batch.map((classId) => `class.sourcedId='${escapeFilterValue2(classId)}'`).join(" OR ");
59577
59638
  const filters = [batch.length > 1 ? `(${classFilter})` : classFilter];
59578
- if (options?.onlyActive ?? true) {
59639
+ if (!options?.includeInactive) {
59579
59640
  filters.push(`status='active'`);
59580
59641
  }
59581
59642
  if (options?.role) {
@@ -123479,15 +123540,19 @@ var init_timeback_controller = __esm(() => {
123479
123540
  getRoster = requireGameManagementAccess(async (ctx) => {
123480
123541
  const gameId = ctx.params.gameId;
123481
123542
  const courseId = ctx.params.courseId;
123543
+ const includeInactive = ctx.url.searchParams.get("includeInactive") === "true";
123482
123544
  if (!gameId || !courseId) {
123483
123545
  throw ApiError.badRequest("Missing gameId or courseId parameter");
123484
123546
  }
123485
123547
  logger64.debug("Getting course roster", {
123486
123548
  requesterId: ctx.user.id,
123487
123549
  gameId,
123488
- courseId
123550
+ courseId,
123551
+ includeInactive
123552
+ });
123553
+ return ctx.services.timebackAdmin.listStudentsForCourse(gameId, courseId, ctx.user, {
123554
+ includeInactive
123489
123555
  });
123490
- return ctx.services.timebackAdmin.listStudentsForCourse(gameId, courseId, ctx.user);
123491
123556
  });
123492
123557
  getStudentOverview = requireGameManagementAccess(async (ctx) => {
123493
123558
  const timebackId = ctx.params.timebackId;
@@ -126480,7 +126545,7 @@ var import_picocolors12 = __toESM(require_picocolors(), 1);
126480
126545
  // package.json
126481
126546
  var package_default2 = {
126482
126547
  name: "@playcademy/vite-plugin",
126483
- version: "0.2.26",
126548
+ version: "0.2.27-beta.1",
126484
126549
  type: "module",
126485
126550
  exports: {
126486
126551
  ".": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playcademy/vite-plugin",
3
- "version": "0.2.26",
3
+ "version": "0.2.27-beta.1",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {