@playcademy/sandbox 0.11.0 → 0.11.1-beta.2
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/README.md +40 -20
- package/dist/cli.js +27 -61
- package/dist/mocks/timeback.d.ts +1 -1
- package/dist/server.js +27 -61
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -199,17 +199,18 @@ bunx @playcademy/sandbox \
|
|
|
199
199
|
--timeback-student-id your-student-sourcedId
|
|
200
200
|
```
|
|
201
201
|
|
|
202
|
-
| Option
|
|
203
|
-
|
|
|
204
|
-
| `--timeback-local`
|
|
205
|
-
| `--timeback-oneroster-url <url>`
|
|
206
|
-
| `--timeback-caliper-url <url>`
|
|
207
|
-
| `--timeback-course-id <id>`
|
|
208
|
-
| `--timeback-student-id <id>`
|
|
209
|
-
| `--timeback-role <role>`
|
|
210
|
-
| `--timeback-org-id <id>`
|
|
211
|
-
| `--timeback-org-name <name>`
|
|
212
|
-
| `--timeback-org-type <type>`
|
|
202
|
+
| Option | Description |
|
|
203
|
+
| :-------------------------------------- | :-------------------------------------------------------------------------------- |
|
|
204
|
+
| `--timeback-local` | Enable local TimeBack mode (Docker) |
|
|
205
|
+
| `--timeback-oneroster-url <url>` | OneRoster API URL |
|
|
206
|
+
| `--timeback-caliper-url <url>` | Caliper API URL |
|
|
207
|
+
| `--timeback-course-id <id>` | Course ID for enrollments |
|
|
208
|
+
| `--timeback-student-id <id>` | Student ID to link to demo user |
|
|
209
|
+
| `--timeback-role <role>` | User role (student, parent, teacher, administrator) |
|
|
210
|
+
| `--timeback-org-id <id>` | Organization ID |
|
|
211
|
+
| `--timeback-org-name <name>` | Organization display name |
|
|
212
|
+
| `--timeback-org-type <type>` | Organization type (school, district, department, etc.) |
|
|
213
|
+
| `--timeback-excluded-courses <courses>` | Comma-separated courses to exclude from enrollment (e.g. `FastMath:3,FastMath:4`) |
|
|
213
214
|
|
|
214
215
|
### Environment Variables
|
|
215
216
|
|
|
@@ -262,15 +263,34 @@ The sandbox provides these TimeBack endpoints (matching production platform):
|
|
|
262
263
|
**Runtime:**
|
|
263
264
|
|
|
264
265
|
- `POST /api/timeback/end-activity` - Submit activity results
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
- `
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
266
|
+
- `POST /api/timeback/heartbeat` - Record incremental activity heartbeat
|
|
267
|
+
- `POST /api/timeback/advance-course` - Advance a student to the next-grade course
|
|
268
|
+
- `POST /api/timeback/unenroll-course` - Unenroll a student from their active course
|
|
269
|
+
- `POST /api/timeback/populate-student` - No-op in the sandbox (always returns `no_record`)
|
|
270
|
+
|
|
271
|
+
**Assessments:**
|
|
272
|
+
|
|
273
|
+
- `POST /api/timeback/assessments/prepare` - Prepare the next assessment attempt
|
|
274
|
+
- `POST /api/timeback/assessments/start` - Start a new assessment attempt
|
|
275
|
+
- `GET /api/timeback/assessments/latest` - Get the user's latest attempt
|
|
276
|
+
- `GET /api/timeback/assessments/:attemptId` - Get a specific attempt
|
|
277
|
+
- `POST /api/timeback/assessments/:attemptId/resume` - Resume an attempt
|
|
278
|
+
- `POST /api/timeback/assessments/:attemptId/progress` - Record mastery progress
|
|
279
|
+
- `POST /api/timeback/assessments/:attemptId/save` - Checkpoint an in-progress attempt
|
|
280
|
+
- `POST /api/timeback/assessments/:attemptId/submit-item` - Score a single item
|
|
281
|
+
- `POST /api/timeback/assessments/:attemptId/submit` - Submit an attempt
|
|
282
|
+
- `POST /api/timeback/assessments/:attemptId/finalize` - Record XP and publish completion
|
|
283
|
+
|
|
284
|
+
**Student Data:**
|
|
285
|
+
|
|
286
|
+
- `GET /api/timeback/user` - Get the current user's TimeBack data
|
|
287
|
+
- `GET /api/timeback/user/enrollments` - Get the current user's enrollments
|
|
288
|
+
- `GET /api/timeback/user/:timebackId` - Get a student's TimeBack data
|
|
289
|
+
- `GET /api/timeback/student-xp/:timebackId` - Get a student's XP
|
|
290
|
+
- `GET /api/timeback/student-mastery/:timebackId` - Get a student's mastery
|
|
291
|
+
- `GET /api/timeback/student-highest-grade-mastered/:timebackId` - Get a student's highest grade mastered
|
|
292
|
+
|
|
293
|
+
These use the same api-core handlers as the production platform, ensuring consistency. The student data routes return mock data when TimeBack runs in mock mode.
|
|
274
294
|
|
|
275
295
|
When properly configured, your game can call `client.timeback.endActivity()` during local development and see real events appear in your TimeBack dashboard.
|
|
276
296
|
|
package/dist/cli.js
CHANGED
|
@@ -1142,7 +1142,7 @@ var package_default;
|
|
|
1142
1142
|
var init_package = __esm(() => {
|
|
1143
1143
|
package_default = {
|
|
1144
1144
|
name: "@playcademy/sandbox",
|
|
1145
|
-
version: "0.11.
|
|
1145
|
+
version: "0.11.1-beta.2",
|
|
1146
1146
|
description: "Local development server for Playcademy game development",
|
|
1147
1147
|
type: "module",
|
|
1148
1148
|
exports: {
|
|
@@ -94381,6 +94381,12 @@ function resolvePrimaryAccountRole(roles2) {
|
|
|
94381
94381
|
}
|
|
94382
94382
|
return ACCOUNT_ROLE_PRECEDENCE.find((role) => roles2.some((entry) => entry.role === role)) ?? null;
|
|
94383
94383
|
}
|
|
94384
|
+
function resolveEnrollableAccountRole(user) {
|
|
94385
|
+
if (user.status === "tobedeleted" || String(user.enabledUser) === "false") {
|
|
94386
|
+
return null;
|
|
94387
|
+
}
|
|
94388
|
+
return resolvePrimaryAccountRole(user.roles?.filter((entry) => entry.status !== "tobedeleted"));
|
|
94389
|
+
}
|
|
94384
94390
|
var ACCOUNT_ROLE_PRECEDENCE;
|
|
94385
94391
|
var init_timeback_admin_util = __esm(() => {
|
|
94386
94392
|
init_errors2();
|
|
@@ -99997,13 +100003,8 @@ class TimebackAdminService {
|
|
|
99997
100003
|
let allUsers = [];
|
|
99998
100004
|
try {
|
|
99999
100005
|
const page = await client2.api.oneroster.users.list({
|
|
100000
|
-
|
|
100001
|
-
|
|
100002
|
-
{ givenName: { contains: trimmedQuery } },
|
|
100003
|
-
{ familyName: { contains: trimmedQuery } },
|
|
100004
|
-
{ email: { contains: trimmedQuery } }
|
|
100005
|
-
]
|
|
100006
|
-
},
|
|
100006
|
+
search: trimmedQuery,
|
|
100007
|
+
where: { status: "active", enabledUser: "true" },
|
|
100007
100008
|
limit: 25
|
|
100008
100009
|
});
|
|
100009
100010
|
allUsers = page.data;
|
|
@@ -100032,7 +100033,7 @@ class TimebackAdminService {
|
|
|
100032
100033
|
pastEnrollmentsByStudent.set(studentId, list);
|
|
100033
100034
|
}
|
|
100034
100035
|
const students = allUsers.flatMap((entry) => {
|
|
100035
|
-
const primaryRole =
|
|
100036
|
+
const primaryRole = resolveEnrollableAccountRole(entry);
|
|
100036
100037
|
if (!entry.sourcedId || !primaryRole) {
|
|
100037
100038
|
return [];
|
|
100038
100039
|
}
|
|
@@ -110366,12 +110367,13 @@ class LeaderboardService {
|
|
|
110366
110367
|
eq(gameScores.gameId, gameId),
|
|
110367
110368
|
eq(users.isAnonymous, isAnonymousUser)
|
|
110368
110369
|
];
|
|
110369
|
-
const bestScoresSubquery = db2.
|
|
110370
|
+
const bestScoresSubquery = db2.selectDistinctOn([gameScores.userId, gameScores.gameId], {
|
|
110370
110371
|
userId: gameScores.userId,
|
|
110371
110372
|
gameId: gameScores.gameId,
|
|
110372
|
-
score:
|
|
110373
|
-
achievedAt:
|
|
110374
|
-
|
|
110373
|
+
score: gameScores.score,
|
|
110374
|
+
achievedAt: gameScores.achievedAt,
|
|
110375
|
+
metadata: gameScores.metadata
|
|
110376
|
+
}).from(gameScores).innerJoin(users, eq(gameScores.userId, users.id)).where(and(...conditions2)).orderBy(gameScores.userId, gameScores.gameId, desc(gameScores.score), asc(gameScores.achievedAt)).as("best_scores");
|
|
110375
110377
|
const scores = await db2.select({
|
|
110376
110378
|
userId: users.id,
|
|
110377
110379
|
username: users.username,
|
|
@@ -110379,11 +110381,11 @@ class LeaderboardService {
|
|
|
110379
110381
|
userImage: users.image,
|
|
110380
110382
|
score: bestScoresSubquery.score,
|
|
110381
110383
|
achievedAt: bestScoresSubquery.achievedAt,
|
|
110382
|
-
metadata:
|
|
110384
|
+
metadata: bestScoresSubquery.metadata,
|
|
110383
110385
|
gameId: games.id,
|
|
110384
110386
|
gameTitle: games.displayName,
|
|
110385
110387
|
gameSlug: games.slug
|
|
110386
|
-
}).from(bestScoresSubquery).innerJoin(users, eq(bestScoresSubquery.userId, users.id)).innerJoin(games, eq(bestScoresSubquery.gameId, games.id)).
|
|
110388
|
+
}).from(bestScoresSubquery).innerJoin(users, eq(bestScoresSubquery.userId, users.id)).innerJoin(games, eq(bestScoresSubquery.gameId, games.id)).orderBy(desc(bestScoresSubquery.score), asc(bestScoresSubquery.achievedAt), asc(bestScoresSubquery.userId)).limit(limit).offset(offset);
|
|
110387
110389
|
setAttributes({
|
|
110388
110390
|
"app.leaderboard.result_count": scores.length,
|
|
110389
110391
|
"app.leaderboard.no_result": scores.length === 0
|
|
@@ -110623,7 +110625,7 @@ class UserService {
|
|
|
110623
110625
|
"app.user.timeback_enriched": false
|
|
110624
110626
|
});
|
|
110625
110627
|
const localDay = await this.resolveLocalDay(userData, observedTimeZone);
|
|
110626
|
-
const timeback2 = userData.timebackId ? await this.
|
|
110628
|
+
const timeback2 = userData.timebackId ? await this.fetchTimebackProfile(userData.timebackId) : undefined;
|
|
110627
110629
|
setAttribute("app.user.timeback_enriched", Boolean(timeback2));
|
|
110628
110630
|
if (gameId) {
|
|
110629
110631
|
return {
|
|
@@ -110728,18 +110730,10 @@ class UserService {
|
|
|
110728
110730
|
observedAt: null
|
|
110729
110731
|
};
|
|
110730
110732
|
}
|
|
110731
|
-
async
|
|
110732
|
-
const
|
|
110733
|
-
|
|
110734
|
-
|
|
110735
|
-
]);
|
|
110736
|
-
const enrollments = gameId ? filterEnrollmentsByGame(allEnrollments, gameId) : allEnrollments;
|
|
110737
|
-
const organizations = gameId ? this.filterOrganizationsByEnrollments(allOrganizations, enrollments) : allOrganizations;
|
|
110738
|
-
setAttributes({
|
|
110739
|
-
"app.timeback.enrollment_count": enrollments.length,
|
|
110740
|
-
"app.timeback.organization_count": organizations.length
|
|
110741
|
-
});
|
|
110742
|
-
return { id: timebackId, role, enrollments, organizations };
|
|
110733
|
+
async fetchTimebackProfile(timebackId) {
|
|
110734
|
+
const { role, organizations } = await withSpan("timeback.fetch_profile", () => this.fetchStudentProfile(timebackId));
|
|
110735
|
+
setAttribute("app.timeback.organization_count", organizations.length);
|
|
110736
|
+
return { id: timebackId, role, organizations };
|
|
110743
110737
|
}
|
|
110744
110738
|
async fetchStudentProfile(timebackId) {
|
|
110745
110739
|
if (!this.deps.timeback) {
|
|
@@ -110778,46 +110772,14 @@ class UserService {
|
|
|
110778
110772
|
return { role: "student", organizations: [] };
|
|
110779
110773
|
}
|
|
110780
110774
|
}
|
|
110781
|
-
async fetchEnrollments(timebackId) {
|
|
110782
|
-
if (!this.deps.timeback) {
|
|
110783
|
-
setAttribute("app.timeback.client", "unavailable");
|
|
110784
|
-
return [];
|
|
110785
|
-
}
|
|
110786
|
-
try {
|
|
110787
|
-
const enrollments = await this.deps.timeback.roster.getEnrollments(timebackId);
|
|
110788
|
-
const courseIds = enrollments.map((e) => e.courseId).filter((id) => Boolean(id));
|
|
110789
|
-
if (courseIds.length === 0) {
|
|
110790
|
-
return [];
|
|
110791
|
-
}
|
|
110792
|
-
const integrations = await this.deps.db.query.gameTimebackIntegrations.findMany({
|
|
110793
|
-
where: and(inArray(gameTimebackIntegrations.courseId, courseIds), isActiveGameTimebackIntegrationStatus())
|
|
110794
|
-
});
|
|
110795
|
-
return mapEnrollmentsToUserEnrollments(enrollments, integrations);
|
|
110796
|
-
} catch (error88) {
|
|
110797
|
-
addEvent("timeback.enrollment_fetch_failed", {
|
|
110798
|
-
"exception.type": errorType(error88),
|
|
110799
|
-
"app.error.message": errorMessage(error88)
|
|
110800
|
-
});
|
|
110801
|
-
return [];
|
|
110802
|
-
}
|
|
110803
|
-
}
|
|
110804
|
-
filterOrganizationsByEnrollments(organizations, enrollments) {
|
|
110805
|
-
const enrollmentOrgIds = new Set(enrollments.map((e) => e.orgId).filter(Boolean));
|
|
110806
|
-
if (enrollmentOrgIds.size === 0) {
|
|
110807
|
-
return [];
|
|
110808
|
-
}
|
|
110809
|
-
return organizations.filter((o) => enrollmentOrgIds.has(o.id));
|
|
110810
|
-
}
|
|
110811
110775
|
}
|
|
110812
110776
|
var LEARNING_TIME_ZONE_REFRESH_INTERVAL_MS;
|
|
110813
110777
|
var init_user_service = __esm(() => {
|
|
110814
110778
|
init_drizzle_orm();
|
|
110815
110779
|
init_src();
|
|
110816
|
-
init_helpers_index();
|
|
110817
110780
|
init_tables_index();
|
|
110818
110781
|
init_spans();
|
|
110819
110782
|
init_errors2();
|
|
110820
|
-
init_timeback_util();
|
|
110821
110783
|
LEARNING_TIME_ZONE_REFRESH_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
110822
110784
|
});
|
|
110823
110785
|
|
|
@@ -169346,8 +169308,12 @@ async function buildLocalDayContext(db2, user, observedTimeZone) {
|
|
|
169346
169308
|
observedAt: null
|
|
169347
169309
|
};
|
|
169348
169310
|
}
|
|
169311
|
+
function getMockTimebackProfile(timebackId) {
|
|
169312
|
+
const { role, organizations } = getMockStudentProfile();
|
|
169313
|
+
return { id: timebackId, role, organizations };
|
|
169314
|
+
}
|
|
169349
169315
|
async function buildMockUserResponse(db2, user, gameId, observedTimeZone) {
|
|
169350
|
-
const timeback3 = user.timebackId ?
|
|
169316
|
+
const timeback3 = user.timebackId ? getMockTimebackProfile(user.timebackId) : undefined;
|
|
169351
169317
|
const localDay = await buildLocalDayContext(db2, user, observedTimeZone);
|
|
169352
169318
|
if (gameId) {
|
|
169353
169319
|
return {
|
package/dist/mocks/timeback.d.ts
CHANGED
|
@@ -32,7 +32,7 @@ export declare function getMockTimebackData(db: DatabaseInstance, timebackId: st
|
|
|
32
32
|
export declare function getMockTimebackUser(db: DatabaseInstance, gameId?: string): Promise<UserTimebackData>;
|
|
33
33
|
export declare function getMockHighestGradeMastered(enrollments: UserEnrollment[], subject: string): number | null;
|
|
34
34
|
/**
|
|
35
|
-
* Build a complete user response with mock timeback
|
|
35
|
+
* Build a complete user response with mock timeback identity.
|
|
36
36
|
* Used to bypass api-core when in mock mode (avoids real API calls).
|
|
37
37
|
*/
|
|
38
38
|
export declare function buildMockUserResponse(db: DatabaseInstance, user: User, gameId?: string, observedTimeZone?: string | null): Promise<AuthenticatedUser | GameUser>;
|
package/dist/server.js
CHANGED
|
@@ -1141,7 +1141,7 @@ var package_default;
|
|
|
1141
1141
|
var init_package = __esm(() => {
|
|
1142
1142
|
package_default = {
|
|
1143
1143
|
name: "@playcademy/sandbox",
|
|
1144
|
-
version: "0.11.
|
|
1144
|
+
version: "0.11.1-beta.2",
|
|
1145
1145
|
description: "Local development server for Playcademy game development",
|
|
1146
1146
|
type: "module",
|
|
1147
1147
|
exports: {
|
|
@@ -94380,6 +94380,12 @@ function resolvePrimaryAccountRole(roles2) {
|
|
|
94380
94380
|
}
|
|
94381
94381
|
return ACCOUNT_ROLE_PRECEDENCE.find((role) => roles2.some((entry) => entry.role === role)) ?? null;
|
|
94382
94382
|
}
|
|
94383
|
+
function resolveEnrollableAccountRole(user) {
|
|
94384
|
+
if (user.status === "tobedeleted" || String(user.enabledUser) === "false") {
|
|
94385
|
+
return null;
|
|
94386
|
+
}
|
|
94387
|
+
return resolvePrimaryAccountRole(user.roles?.filter((entry) => entry.status !== "tobedeleted"));
|
|
94388
|
+
}
|
|
94383
94389
|
var ACCOUNT_ROLE_PRECEDENCE;
|
|
94384
94390
|
var init_timeback_admin_util = __esm(() => {
|
|
94385
94391
|
init_errors2();
|
|
@@ -99996,13 +100002,8 @@ class TimebackAdminService {
|
|
|
99996
100002
|
let allUsers = [];
|
|
99997
100003
|
try {
|
|
99998
100004
|
const page = await client2.api.oneroster.users.list({
|
|
99999
|
-
|
|
100000
|
-
|
|
100001
|
-
{ givenName: { contains: trimmedQuery } },
|
|
100002
|
-
{ familyName: { contains: trimmedQuery } },
|
|
100003
|
-
{ email: { contains: trimmedQuery } }
|
|
100004
|
-
]
|
|
100005
|
-
},
|
|
100005
|
+
search: trimmedQuery,
|
|
100006
|
+
where: { status: "active", enabledUser: "true" },
|
|
100006
100007
|
limit: 25
|
|
100007
100008
|
});
|
|
100008
100009
|
allUsers = page.data;
|
|
@@ -100031,7 +100032,7 @@ class TimebackAdminService {
|
|
|
100031
100032
|
pastEnrollmentsByStudent.set(studentId, list);
|
|
100032
100033
|
}
|
|
100033
100034
|
const students = allUsers.flatMap((entry) => {
|
|
100034
|
-
const primaryRole =
|
|
100035
|
+
const primaryRole = resolveEnrollableAccountRole(entry);
|
|
100035
100036
|
if (!entry.sourcedId || !primaryRole) {
|
|
100036
100037
|
return [];
|
|
100037
100038
|
}
|
|
@@ -110365,12 +110366,13 @@ class LeaderboardService {
|
|
|
110365
110366
|
eq(gameScores.gameId, gameId),
|
|
110366
110367
|
eq(users.isAnonymous, isAnonymousUser)
|
|
110367
110368
|
];
|
|
110368
|
-
const bestScoresSubquery = db2.
|
|
110369
|
+
const bestScoresSubquery = db2.selectDistinctOn([gameScores.userId, gameScores.gameId], {
|
|
110369
110370
|
userId: gameScores.userId,
|
|
110370
110371
|
gameId: gameScores.gameId,
|
|
110371
|
-
score:
|
|
110372
|
-
achievedAt:
|
|
110373
|
-
|
|
110372
|
+
score: gameScores.score,
|
|
110373
|
+
achievedAt: gameScores.achievedAt,
|
|
110374
|
+
metadata: gameScores.metadata
|
|
110375
|
+
}).from(gameScores).innerJoin(users, eq(gameScores.userId, users.id)).where(and(...conditions2)).orderBy(gameScores.userId, gameScores.gameId, desc(gameScores.score), asc(gameScores.achievedAt)).as("best_scores");
|
|
110374
110376
|
const scores = await db2.select({
|
|
110375
110377
|
userId: users.id,
|
|
110376
110378
|
username: users.username,
|
|
@@ -110378,11 +110380,11 @@ class LeaderboardService {
|
|
|
110378
110380
|
userImage: users.image,
|
|
110379
110381
|
score: bestScoresSubquery.score,
|
|
110380
110382
|
achievedAt: bestScoresSubquery.achievedAt,
|
|
110381
|
-
metadata:
|
|
110383
|
+
metadata: bestScoresSubquery.metadata,
|
|
110382
110384
|
gameId: games.id,
|
|
110383
110385
|
gameTitle: games.displayName,
|
|
110384
110386
|
gameSlug: games.slug
|
|
110385
|
-
}).from(bestScoresSubquery).innerJoin(users, eq(bestScoresSubquery.userId, users.id)).innerJoin(games, eq(bestScoresSubquery.gameId, games.id)).
|
|
110387
|
+
}).from(bestScoresSubquery).innerJoin(users, eq(bestScoresSubquery.userId, users.id)).innerJoin(games, eq(bestScoresSubquery.gameId, games.id)).orderBy(desc(bestScoresSubquery.score), asc(bestScoresSubquery.achievedAt), asc(bestScoresSubquery.userId)).limit(limit).offset(offset);
|
|
110386
110388
|
setAttributes({
|
|
110387
110389
|
"app.leaderboard.result_count": scores.length,
|
|
110388
110390
|
"app.leaderboard.no_result": scores.length === 0
|
|
@@ -110622,7 +110624,7 @@ class UserService {
|
|
|
110622
110624
|
"app.user.timeback_enriched": false
|
|
110623
110625
|
});
|
|
110624
110626
|
const localDay = await this.resolveLocalDay(userData, observedTimeZone);
|
|
110625
|
-
const timeback2 = userData.timebackId ? await this.
|
|
110627
|
+
const timeback2 = userData.timebackId ? await this.fetchTimebackProfile(userData.timebackId) : undefined;
|
|
110626
110628
|
setAttribute("app.user.timeback_enriched", Boolean(timeback2));
|
|
110627
110629
|
if (gameId) {
|
|
110628
110630
|
return {
|
|
@@ -110727,18 +110729,10 @@ class UserService {
|
|
|
110727
110729
|
observedAt: null
|
|
110728
110730
|
};
|
|
110729
110731
|
}
|
|
110730
|
-
async
|
|
110731
|
-
const
|
|
110732
|
-
|
|
110733
|
-
|
|
110734
|
-
]);
|
|
110735
|
-
const enrollments = gameId ? filterEnrollmentsByGame(allEnrollments, gameId) : allEnrollments;
|
|
110736
|
-
const organizations = gameId ? this.filterOrganizationsByEnrollments(allOrganizations, enrollments) : allOrganizations;
|
|
110737
|
-
setAttributes({
|
|
110738
|
-
"app.timeback.enrollment_count": enrollments.length,
|
|
110739
|
-
"app.timeback.organization_count": organizations.length
|
|
110740
|
-
});
|
|
110741
|
-
return { id: timebackId, role, enrollments, organizations };
|
|
110732
|
+
async fetchTimebackProfile(timebackId) {
|
|
110733
|
+
const { role, organizations } = await withSpan("timeback.fetch_profile", () => this.fetchStudentProfile(timebackId));
|
|
110734
|
+
setAttribute("app.timeback.organization_count", organizations.length);
|
|
110735
|
+
return { id: timebackId, role, organizations };
|
|
110742
110736
|
}
|
|
110743
110737
|
async fetchStudentProfile(timebackId) {
|
|
110744
110738
|
if (!this.deps.timeback) {
|
|
@@ -110777,46 +110771,14 @@ class UserService {
|
|
|
110777
110771
|
return { role: "student", organizations: [] };
|
|
110778
110772
|
}
|
|
110779
110773
|
}
|
|
110780
|
-
async fetchEnrollments(timebackId) {
|
|
110781
|
-
if (!this.deps.timeback) {
|
|
110782
|
-
setAttribute("app.timeback.client", "unavailable");
|
|
110783
|
-
return [];
|
|
110784
|
-
}
|
|
110785
|
-
try {
|
|
110786
|
-
const enrollments = await this.deps.timeback.roster.getEnrollments(timebackId);
|
|
110787
|
-
const courseIds = enrollments.map((e) => e.courseId).filter((id) => Boolean(id));
|
|
110788
|
-
if (courseIds.length === 0) {
|
|
110789
|
-
return [];
|
|
110790
|
-
}
|
|
110791
|
-
const integrations = await this.deps.db.query.gameTimebackIntegrations.findMany({
|
|
110792
|
-
where: and(inArray(gameTimebackIntegrations.courseId, courseIds), isActiveGameTimebackIntegrationStatus())
|
|
110793
|
-
});
|
|
110794
|
-
return mapEnrollmentsToUserEnrollments(enrollments, integrations);
|
|
110795
|
-
} catch (error88) {
|
|
110796
|
-
addEvent("timeback.enrollment_fetch_failed", {
|
|
110797
|
-
"exception.type": errorType(error88),
|
|
110798
|
-
"app.error.message": errorMessage(error88)
|
|
110799
|
-
});
|
|
110800
|
-
return [];
|
|
110801
|
-
}
|
|
110802
|
-
}
|
|
110803
|
-
filterOrganizationsByEnrollments(organizations, enrollments) {
|
|
110804
|
-
const enrollmentOrgIds = new Set(enrollments.map((e) => e.orgId).filter(Boolean));
|
|
110805
|
-
if (enrollmentOrgIds.size === 0) {
|
|
110806
|
-
return [];
|
|
110807
|
-
}
|
|
110808
|
-
return organizations.filter((o) => enrollmentOrgIds.has(o.id));
|
|
110809
|
-
}
|
|
110810
110774
|
}
|
|
110811
110775
|
var LEARNING_TIME_ZONE_REFRESH_INTERVAL_MS;
|
|
110812
110776
|
var init_user_service = __esm(() => {
|
|
110813
110777
|
init_drizzle_orm();
|
|
110814
110778
|
init_src();
|
|
110815
|
-
init_helpers_index();
|
|
110816
110779
|
init_tables_index();
|
|
110817
110780
|
init_spans();
|
|
110818
110781
|
init_errors2();
|
|
110819
|
-
init_timeback_util();
|
|
110820
110782
|
LEARNING_TIME_ZONE_REFRESH_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
110821
110783
|
});
|
|
110822
110784
|
|
|
@@ -169345,8 +169307,12 @@ async function buildLocalDayContext(db2, user, observedTimeZone) {
|
|
|
169345
169307
|
observedAt: null
|
|
169346
169308
|
};
|
|
169347
169309
|
}
|
|
169310
|
+
function getMockTimebackProfile(timebackId) {
|
|
169311
|
+
const { role, organizations } = getMockStudentProfile();
|
|
169312
|
+
return { id: timebackId, role, organizations };
|
|
169313
|
+
}
|
|
169348
169314
|
async function buildMockUserResponse(db2, user, gameId, observedTimeZone) {
|
|
169349
|
-
const timeback3 = user.timebackId ?
|
|
169315
|
+
const timeback3 = user.timebackId ? getMockTimebackProfile(user.timebackId) : undefined;
|
|
169350
169316
|
const localDay = await buildLocalDayContext(db2, user, observedTimeZone);
|
|
169351
169317
|
if (gameId) {
|
|
169352
169318
|
return {
|