@playcademy/vite-plugin 1.1.1-beta.2 → 1.1.1-beta.3
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 +1776 -1175
- 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: "1.1.1-beta.
|
|
23749
|
+
version: "1.1.1-beta.3",
|
|
23750
23750
|
type: "module",
|
|
23751
23751
|
exports: {
|
|
23752
23752
|
".": {
|
|
@@ -24244,6 +24244,7 @@ import { join as join2 } from "path";
|
|
|
24244
24244
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
24245
24245
|
import { gzipSync } from "node:zlib";
|
|
24246
24246
|
import { stdout } from "process";
|
|
24247
|
+
import { createHash } from "node:crypto";
|
|
24247
24248
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
24248
24249
|
import crypto3 from "node:crypto";
|
|
24249
24250
|
import * as s3 from "fs";
|
|
@@ -25342,7 +25343,7 @@ var package_default2;
|
|
|
25342
25343
|
var init_package = __esm(() => {
|
|
25343
25344
|
package_default2 = {
|
|
25344
25345
|
name: "@playcademy/sandbox",
|
|
25345
|
-
version: "0.5.2-beta.
|
|
25346
|
+
version: "0.5.2-beta.3",
|
|
25346
25347
|
description: "Local development server for Playcademy game development",
|
|
25347
25348
|
type: "module",
|
|
25348
25349
|
exports: {
|
|
@@ -35608,25 +35609,34 @@ var init_table6 = __esm(() => {
|
|
|
35608
35609
|
})
|
|
35609
35610
|
}));
|
|
35610
35611
|
});
|
|
35612
|
+
var gameTimebackIntegrationStatusEnum;
|
|
35611
35613
|
var gameTimebackIntegrations;
|
|
35612
35614
|
var gameTimebackAssessmentTests;
|
|
35613
35615
|
var gameTimebackMetricDiscrepancyVerifications;
|
|
35614
35616
|
var init_table7 = __esm(() => {
|
|
35617
|
+
init_drizzle_orm();
|
|
35615
35618
|
init_pg_core();
|
|
35616
35619
|
init_table5();
|
|
35617
35620
|
init_table3();
|
|
35621
|
+
gameTimebackIntegrationStatusEnum = pgEnum("game_timeback_integration_status", [
|
|
35622
|
+
"active",
|
|
35623
|
+
"deactivated"
|
|
35624
|
+
]);
|
|
35618
35625
|
gameTimebackIntegrations = pgTable("game_timeback_integrations", {
|
|
35619
35626
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
35620
35627
|
gameId: uuid("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
|
|
35621
35628
|
courseId: text("course_id").notNull(),
|
|
35622
35629
|
grade: integer("grade").notNull(),
|
|
35623
35630
|
subject: text("subject").notNull(),
|
|
35631
|
+
status: gameTimebackIntegrationStatusEnum("status"),
|
|
35624
35632
|
totalXp: integer("total_xp"),
|
|
35625
35633
|
lastVerifiedAt: timestamp("last_verified_at", { withTimezone: true }),
|
|
35634
|
+
deactivatedAt: timestamp("deactivated_at", { withTimezone: true }),
|
|
35635
|
+
reactivatedAt: timestamp("reactivated_at", { withTimezone: true }),
|
|
35626
35636
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
35627
35637
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
35628
35638
|
}, (table3) => [
|
|
35629
|
-
uniqueIndex("game_timeback_integrations_game_grade_subject_idx").on(table3.gameId, table3.grade, table3.subject)
|
|
35639
|
+
uniqueIndex("game_timeback_integrations_game_grade_subject_idx").on(table3.gameId, table3.grade, table3.subject).where(sql`${table3.status} IS DISTINCT FROM 'deactivated'`)
|
|
35630
35640
|
]);
|
|
35631
35641
|
gameTimebackAssessmentTests = pgTable("game_timeback_assessment_tests", {
|
|
35632
35642
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
@@ -35667,6 +35677,7 @@ __export(exports_tables_index, {
|
|
|
35667
35677
|
gameTypeEnum: () => gameTypeEnum,
|
|
35668
35678
|
gameTimebackMetricDiscrepancyVerifications: () => gameTimebackMetricDiscrepancyVerifications,
|
|
35669
35679
|
gameTimebackIntegrations: () => gameTimebackIntegrations,
|
|
35680
|
+
gameTimebackIntegrationStatusEnum: () => gameTimebackIntegrationStatusEnum,
|
|
35670
35681
|
gameTimebackAssessmentTests: () => gameTimebackAssessmentTests,
|
|
35671
35682
|
gameScoresRelations: () => gameScoresRelations,
|
|
35672
35683
|
gameScores: () => gameScores,
|
|
@@ -48677,6 +48688,7 @@ var init_helpers = __esm(() => {
|
|
|
48677
48688
|
init_mime();
|
|
48678
48689
|
});
|
|
48679
48690
|
var init_provider = __esm(() => {
|
|
48691
|
+
init_src();
|
|
48680
48692
|
init_src();
|
|
48681
48693
|
init_core();
|
|
48682
48694
|
init_constants2();
|
|
@@ -51748,21 +51760,626 @@ var init_game_member_service = __esm(() => {
|
|
|
51748
51760
|
init_spans();
|
|
51749
51761
|
init_errors();
|
|
51750
51762
|
});
|
|
51763
|
+
function isActiveGameTimebackIntegrationStatus(statusColumn = gameTimebackIntegrations.status) {
|
|
51764
|
+
return sql`${statusColumn} IS DISTINCT FROM ${DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS}`;
|
|
51765
|
+
}
|
|
51766
|
+
var ACTIVE_GAME_TIMEBACK_INTEGRATION_STATUS = "active";
|
|
51767
|
+
var DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS = "deactivated";
|
|
51768
|
+
var init_helpers2 = __esm(() => {
|
|
51769
|
+
init_drizzle_orm();
|
|
51770
|
+
init_table7();
|
|
51771
|
+
});
|
|
51772
|
+
var init_helpers_index = __esm(() => {
|
|
51773
|
+
init_helpers2();
|
|
51774
|
+
});
|
|
51751
51775
|
function sleep(ms) {
|
|
51752
51776
|
if (ms <= 0) {
|
|
51753
51777
|
return Promise.resolve();
|
|
51754
51778
|
}
|
|
51755
51779
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
51756
51780
|
}
|
|
51781
|
+
function isObject(value) {
|
|
51782
|
+
return typeof value === "object" && value !== null;
|
|
51783
|
+
}
|
|
51784
|
+
function isCourseMetadata(value) {
|
|
51785
|
+
return isObject(value);
|
|
51786
|
+
}
|
|
51787
|
+
function isResourceMetadata(value) {
|
|
51788
|
+
return isObject(value);
|
|
51789
|
+
}
|
|
51790
|
+
function isPlaycademyResourceMetadata(value) {
|
|
51791
|
+
if (!isObject(value)) {
|
|
51792
|
+
return false;
|
|
51793
|
+
}
|
|
51794
|
+
if (!("mastery" in value) || value.mastery === undefined) {
|
|
51795
|
+
return true;
|
|
51796
|
+
}
|
|
51797
|
+
return isObject(value.mastery);
|
|
51798
|
+
}
|
|
51799
|
+
function isTimebackSubject(value) {
|
|
51800
|
+
return typeof value === "string" && SUBJECT_VALUES.includes(value);
|
|
51801
|
+
}
|
|
51802
|
+
function isTimebackGrade(value) {
|
|
51803
|
+
return typeof value === "number" && Number.isInteger(value) && GRADE_VALUES.includes(value);
|
|
51804
|
+
}
|
|
51805
|
+
var __esm2 = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
51806
|
+
var TIMEBACK_API_URLS;
|
|
51807
|
+
var TIMEBACK_AUTH_URLS;
|
|
51808
|
+
var CALIPER_API_URLS;
|
|
51809
|
+
var ONEROSTER_ENDPOINTS;
|
|
51810
|
+
var QTI_ENDPOINTS;
|
|
51811
|
+
var CALIPER_ENDPOINTS;
|
|
51812
|
+
var CALIPER_CONSTANTS;
|
|
51813
|
+
var TIMEBACK_EVENT_TYPES;
|
|
51814
|
+
var TIMEBACK_ACTIONS;
|
|
51815
|
+
var TIMEBACK_TYPES;
|
|
51816
|
+
var ACTIVITY_METRIC_TYPES;
|
|
51817
|
+
var TIME_METRIC_TYPES;
|
|
51818
|
+
var TIMEBACK_SUBJECTS;
|
|
51819
|
+
var TIMEBACK_GRADE_LEVELS;
|
|
51820
|
+
var TIMEBACK_GRADE_LEVEL_LABELS;
|
|
51821
|
+
var CALIPER_SUBJECTS;
|
|
51822
|
+
var ONEROSTER_STATUS;
|
|
51823
|
+
var SCORE_STATUS;
|
|
51824
|
+
var ENV_VARS;
|
|
51825
|
+
var HTTP_DEFAULTS;
|
|
51826
|
+
var AUTH_DEFAULTS;
|
|
51827
|
+
var CACHE_DEFAULTS;
|
|
51828
|
+
var CONFIG_DEFAULTS;
|
|
51829
|
+
var PLAYCADEMY_DEFAULTS;
|
|
51830
|
+
var RESOURCE_DEFAULTS;
|
|
51831
|
+
var HTTP_STATUS;
|
|
51832
|
+
var ERROR_NAMES;
|
|
51833
|
+
var init_constants3;
|
|
51834
|
+
var SUBJECT_VALUES;
|
|
51835
|
+
var GRADE_VALUES;
|
|
51836
|
+
var init_types2 = __esm(() => {
|
|
51837
|
+
init_src();
|
|
51838
|
+
init_constants3 = __esm2(() => {
|
|
51839
|
+
TIMEBACK_API_URLS = {
|
|
51840
|
+
production: "https://api.alpha-1edtech.ai",
|
|
51841
|
+
staging: "https://api.staging.alpha-1edtech.com"
|
|
51842
|
+
};
|
|
51843
|
+
TIMEBACK_AUTH_URLS = {
|
|
51844
|
+
production: "https://prod-beyond-timeback-api-2-idp.auth.us-east-1.amazoncognito.com",
|
|
51845
|
+
staging: "https://alpha-auth-development-idp.auth.us-west-2.amazoncognito.com"
|
|
51846
|
+
};
|
|
51847
|
+
CALIPER_API_URLS = {
|
|
51848
|
+
production: "https://caliper.alpha-1edtech.ai",
|
|
51849
|
+
staging: "https://caliper-staging.alpha-1edtech.com"
|
|
51850
|
+
};
|
|
51851
|
+
ONEROSTER_ENDPOINTS = {
|
|
51852
|
+
organizations: "/ims/oneroster/rostering/v1p2/orgs",
|
|
51853
|
+
courses: "/ims/oneroster/rostering/v1p2/courses",
|
|
51854
|
+
courseComponents: "/ims/oneroster/rostering/v1p2/courses/components",
|
|
51855
|
+
resources: "/ims/oneroster/resources/v1p2/resources",
|
|
51856
|
+
componentResources: "/ims/oneroster/rostering/v1p2/courses/component-resources",
|
|
51857
|
+
classes: "/ims/oneroster/rostering/v1p2/classes",
|
|
51858
|
+
enrollments: "/ims/oneroster/rostering/v1p2/enrollments",
|
|
51859
|
+
assessmentLineItems: "/ims/oneroster/gradebook/v1p2/assessmentLineItems",
|
|
51860
|
+
assessmentResults: "/ims/oneroster/gradebook/v1p2/assessmentResults",
|
|
51861
|
+
users: "/ims/oneroster/rostering/v1p2/users"
|
|
51862
|
+
};
|
|
51863
|
+
QTI_ENDPOINTS = {
|
|
51864
|
+
assessmentTests: "/assessment-tests",
|
|
51865
|
+
assessmentItems: "/assessment-items"
|
|
51866
|
+
};
|
|
51867
|
+
CALIPER_ENDPOINTS = {
|
|
51868
|
+
event: "/caliper/event",
|
|
51869
|
+
events: "/caliper/events",
|
|
51870
|
+
validate: "/caliper/event/validate"
|
|
51871
|
+
};
|
|
51872
|
+
CALIPER_CONSTANTS = {
|
|
51873
|
+
context: "http://purl.imsglobal.org/ctx/caliper/v1p2",
|
|
51874
|
+
profile: "TimebackProfile",
|
|
51875
|
+
dataVersion: "http://purl.imsglobal.org/ctx/caliper/v1p2"
|
|
51876
|
+
};
|
|
51877
|
+
TIMEBACK_EVENT_TYPES = {
|
|
51878
|
+
activityEvent: "ActivityEvent",
|
|
51879
|
+
timeSpentEvent: "TimeSpentEvent"
|
|
51880
|
+
};
|
|
51881
|
+
TIMEBACK_ACTIONS = {
|
|
51882
|
+
completed: "Completed",
|
|
51883
|
+
spentTime: "SpentTime"
|
|
51884
|
+
};
|
|
51885
|
+
TIMEBACK_TYPES = {
|
|
51886
|
+
user: "TimebackUser",
|
|
51887
|
+
activityContext: "TimebackActivityContext",
|
|
51888
|
+
activityMetricsCollection: "TimebackActivityMetricsCollection",
|
|
51889
|
+
timeSpentMetricsCollection: "TimebackTimeSpentMetricsCollection"
|
|
51890
|
+
};
|
|
51891
|
+
ACTIVITY_METRIC_TYPES = {
|
|
51892
|
+
totalQuestions: "totalQuestions",
|
|
51893
|
+
correctQuestions: "correctQuestions",
|
|
51894
|
+
xpEarned: "xpEarned",
|
|
51895
|
+
masteredUnits: "masteredUnits"
|
|
51896
|
+
};
|
|
51897
|
+
TIME_METRIC_TYPES = {
|
|
51898
|
+
active: "active",
|
|
51899
|
+
inactive: "inactive",
|
|
51900
|
+
waste: "waste",
|
|
51901
|
+
unknown: "unknown",
|
|
51902
|
+
antiPattern: "anti-pattern"
|
|
51903
|
+
};
|
|
51904
|
+
TIMEBACK_SUBJECTS = [
|
|
51905
|
+
"Math",
|
|
51906
|
+
"FastMath",
|
|
51907
|
+
"Science",
|
|
51908
|
+
"Social Studies",
|
|
51909
|
+
"Language",
|
|
51910
|
+
"Reading",
|
|
51911
|
+
"Vocabulary",
|
|
51912
|
+
"Writing"
|
|
51913
|
+
];
|
|
51914
|
+
TIMEBACK_GRADE_LEVELS = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
|
|
51915
|
+
TIMEBACK_GRADE_LEVEL_LABELS = {
|
|
51916
|
+
"-1": "pre-k",
|
|
51917
|
+
"0": "kindergarten",
|
|
51918
|
+
"1": "1st grade",
|
|
51919
|
+
"2": "2nd grade",
|
|
51920
|
+
"3": "3rd grade",
|
|
51921
|
+
"4": "4th grade",
|
|
51922
|
+
"5": "5th grade",
|
|
51923
|
+
"6": "6th grade",
|
|
51924
|
+
"7": "7th grade",
|
|
51925
|
+
"8": "8th grade",
|
|
51926
|
+
"9": "9th grade",
|
|
51927
|
+
"10": "10th grade",
|
|
51928
|
+
"11": "11th grade",
|
|
51929
|
+
"12": "12th grade",
|
|
51930
|
+
"13": "AP"
|
|
51931
|
+
};
|
|
51932
|
+
CALIPER_SUBJECTS = {
|
|
51933
|
+
Reading: "Reading",
|
|
51934
|
+
Language: "Language",
|
|
51935
|
+
Vocabulary: "Vocabulary",
|
|
51936
|
+
SocialStudies: "Social Studies",
|
|
51937
|
+
Writing: "Writing",
|
|
51938
|
+
Science: "Science",
|
|
51939
|
+
FastMath: "FastMath",
|
|
51940
|
+
Math: "Math",
|
|
51941
|
+
None: "None"
|
|
51942
|
+
};
|
|
51943
|
+
ONEROSTER_STATUS = {
|
|
51944
|
+
active: "active",
|
|
51945
|
+
toBeDeleted: "tobedeleted"
|
|
51946
|
+
};
|
|
51947
|
+
SCORE_STATUS = {
|
|
51948
|
+
exempt: "exempt",
|
|
51949
|
+
fullyGraded: "fully graded",
|
|
51950
|
+
notSubmitted: "not submitted",
|
|
51951
|
+
partiallyGraded: "partially graded",
|
|
51952
|
+
submitted: "submitted"
|
|
51953
|
+
};
|
|
51954
|
+
ENV_VARS = {
|
|
51955
|
+
clientId: "TIMEBACK_CLIENT_ID",
|
|
51956
|
+
clientSecret: "TIMEBACK_CLIENT_SECRET",
|
|
51957
|
+
baseUrl: "TIMEBACK_BASE_URL",
|
|
51958
|
+
environment: "TIMEBACK_ENVIRONMENT",
|
|
51959
|
+
vendorResourceId: "TIMEBACK_VENDOR_RESOURCE_ID",
|
|
51960
|
+
launchBaseUrl: "GAME_URL",
|
|
51961
|
+
qtiClientId: "QTI_CLIENT_ID",
|
|
51962
|
+
qtiClientSecret: "QTI_CLIENT_SECRET",
|
|
51963
|
+
qtiAuthUrl: "QTI_AUTH_URL"
|
|
51964
|
+
};
|
|
51965
|
+
HTTP_DEFAULTS = {
|
|
51966
|
+
timeout: 30000,
|
|
51967
|
+
retries: 3,
|
|
51968
|
+
retryBackoffBase: 2
|
|
51969
|
+
};
|
|
51970
|
+
AUTH_DEFAULTS = {
|
|
51971
|
+
tokenCacheDuration: 50000
|
|
51972
|
+
};
|
|
51973
|
+
CACHE_DEFAULTS = {
|
|
51974
|
+
defaultTTL: 600000,
|
|
51975
|
+
defaultMaxSize: 500,
|
|
51976
|
+
defaultName: "TimebackCache",
|
|
51977
|
+
studentTTL: 600000,
|
|
51978
|
+
studentMaxSize: 500,
|
|
51979
|
+
assessmentTTL: 1800000,
|
|
51980
|
+
assessmentMaxSize: 200,
|
|
51981
|
+
enrollmentTTL: 5000,
|
|
51982
|
+
enrollmentMaxSize: 100
|
|
51983
|
+
};
|
|
51984
|
+
CONFIG_DEFAULTS = {
|
|
51985
|
+
fileNames: ["timeback.config.js", "timeback.config.json"]
|
|
51986
|
+
};
|
|
51987
|
+
PLAYCADEMY_DEFAULTS = {
|
|
51988
|
+
organization: TIMEBACK_ORG_SOURCED_ID,
|
|
51989
|
+
launchBaseUrls: PLAYCADEMY_BASE_URLS
|
|
51990
|
+
};
|
|
51991
|
+
RESOURCE_DEFAULTS = {
|
|
51992
|
+
organization: {
|
|
51993
|
+
name: TIMEBACK_ORG_NAME,
|
|
51994
|
+
type: TIMEBACK_ORG_TYPE
|
|
51995
|
+
},
|
|
51996
|
+
course: {
|
|
51997
|
+
gradingScheme: TIMEBACK_COURSE_DEFAULTS.gradingScheme,
|
|
51998
|
+
level: TIMEBACK_COURSE_DEFAULTS.level,
|
|
51999
|
+
metadata: {
|
|
52000
|
+
goals: TIMEBACK_COURSE_DEFAULTS.goals,
|
|
52001
|
+
metrics: TIMEBACK_COURSE_DEFAULTS.metrics
|
|
52002
|
+
}
|
|
52003
|
+
},
|
|
52004
|
+
component: TIMEBACK_COMPONENT_DEFAULTS,
|
|
52005
|
+
resource: TIMEBACK_RESOURCE_DEFAULTS,
|
|
52006
|
+
componentResource: TIMEBACK_COMPONENT_RESOURCE_DEFAULTS
|
|
52007
|
+
};
|
|
52008
|
+
HTTP_STATUS = {
|
|
52009
|
+
CLIENT_ERROR_MIN: 400,
|
|
52010
|
+
CLIENT_ERROR_MAX: 500,
|
|
52011
|
+
SERVER_ERROR_MIN: 500
|
|
52012
|
+
};
|
|
52013
|
+
ERROR_NAMES = {
|
|
52014
|
+
timebackAuth: "TimebackAuthError",
|
|
52015
|
+
timebackApi: "TimebackApiError",
|
|
52016
|
+
timebackConfig: "TimebackConfigError",
|
|
52017
|
+
timebackSdk: "TimebackSDKError"
|
|
52018
|
+
};
|
|
52019
|
+
});
|
|
52020
|
+
init_constants3();
|
|
52021
|
+
SUBJECT_VALUES = TIMEBACK_SUBJECTS;
|
|
52022
|
+
GRADE_VALUES = TIMEBACK_GRADE_LEVELS;
|
|
52023
|
+
});
|
|
52024
|
+
function kebabToTitleCase(kebabStr) {
|
|
52025
|
+
return kebabStr.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
52026
|
+
}
|
|
52027
|
+
function isRecord2(value) {
|
|
52028
|
+
return typeof value === "object" && value !== null;
|
|
52029
|
+
}
|
|
52030
|
+
function filterEnrollmentsByGame(enrollments, gameId) {
|
|
52031
|
+
return enrollments.filter((enrollment) => enrollment.gameId === gameId).map(({ gameId: _2, ...enrollment }) => enrollment);
|
|
52032
|
+
}
|
|
52033
|
+
function mapEnrollmentsToUserEnrollments(enrollments, integrations) {
|
|
52034
|
+
const enrollmentByCourse = new Map(enrollments.map((enrollment) => [enrollment.courseId, enrollment]));
|
|
52035
|
+
const courseToSchool = new Map(enrollments.filter((enrollment) => enrollment.school?.id).map((enrollment) => [enrollment.courseId, enrollment.school.id]));
|
|
52036
|
+
return integrations.map((integration) => {
|
|
52037
|
+
const enrollment = enrollmentByCourse.get(integration.courseId);
|
|
52038
|
+
return {
|
|
52039
|
+
gameId: integration.gameId,
|
|
52040
|
+
grade: integration.grade,
|
|
52041
|
+
subject: integration.subject,
|
|
52042
|
+
courseId: integration.courseId,
|
|
52043
|
+
orgId: courseToSchool.get(integration.courseId),
|
|
52044
|
+
...enrollment ? { id: enrollment.sourcedId } : {}
|
|
52045
|
+
};
|
|
52046
|
+
});
|
|
52047
|
+
}
|
|
52048
|
+
function buildGameTimebackSummaries(integrations) {
|
|
52049
|
+
const summaries = {};
|
|
52050
|
+
for (const integration of integrations) {
|
|
52051
|
+
const summary = summaries[integration.gameId] ?? {
|
|
52052
|
+
subject: null,
|
|
52053
|
+
hasActiveIntegration: false,
|
|
52054
|
+
hasRemovedIntegration: false
|
|
52055
|
+
};
|
|
52056
|
+
const isRemoved = integration.status === DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS;
|
|
52057
|
+
if (isRemoved) {
|
|
52058
|
+
summary.hasRemovedIntegration = true;
|
|
52059
|
+
} else {
|
|
52060
|
+
summary.hasActiveIntegration = true;
|
|
52061
|
+
summary.subject ??= integration.subject;
|
|
52062
|
+
}
|
|
52063
|
+
summaries[integration.gameId] = summary;
|
|
52064
|
+
}
|
|
52065
|
+
return summaries;
|
|
52066
|
+
}
|
|
52067
|
+
function getStringValue(value) {
|
|
52068
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
|
52069
|
+
}
|
|
52070
|
+
function parseSourcedIdFromUrl(url) {
|
|
52071
|
+
if (!url) {
|
|
52072
|
+
return;
|
|
52073
|
+
}
|
|
52074
|
+
const trimmed = url.trim().replace(/\/$/, "");
|
|
52075
|
+
if (!trimmed) {
|
|
52076
|
+
return;
|
|
52077
|
+
}
|
|
52078
|
+
const segments = trimmed.split("/");
|
|
52079
|
+
const lastSegment = segments.at(-1);
|
|
52080
|
+
return lastSegment ? decodeURIComponent(lastSegment) : undefined;
|
|
52081
|
+
}
|
|
52082
|
+
function getGeneratedMetricValue(event, type) {
|
|
52083
|
+
const items = event.generated?.items;
|
|
52084
|
+
if (!Array.isArray(items)) {
|
|
52085
|
+
return;
|
|
52086
|
+
}
|
|
52087
|
+
const metric = items.find((item) => item?.type === type);
|
|
52088
|
+
if (!metric) {
|
|
52089
|
+
return;
|
|
52090
|
+
}
|
|
52091
|
+
const value = typeof metric.value === "number" ? metric.value : Number(metric.value);
|
|
52092
|
+
return Number.isFinite(value) ? value : undefined;
|
|
52093
|
+
}
|
|
52094
|
+
function getMergedCaliperExtensions(event) {
|
|
52095
|
+
const objectActivityExtensions = isRecord2(event.object.activity?.extensions) ? event.object.activity.extensions : undefined;
|
|
52096
|
+
const generatedExtensions = isRecord2(event.generated?.extensions) ? event.generated.extensions : undefined;
|
|
52097
|
+
const eventExtensions = isRecord2(event.extensions) ? event.extensions : undefined;
|
|
52098
|
+
return {
|
|
52099
|
+
...objectActivityExtensions,
|
|
52100
|
+
...generatedExtensions,
|
|
52101
|
+
...eventExtensions
|
|
52102
|
+
};
|
|
52103
|
+
}
|
|
52104
|
+
function getPlaycademyMetadata(event) {
|
|
52105
|
+
const extensions = getMergedCaliperExtensions(event);
|
|
52106
|
+
return isRecord2(extensions.playcademy) ? extensions.playcademy : undefined;
|
|
52107
|
+
}
|
|
52108
|
+
function getActivityId(event, playcademy) {
|
|
52109
|
+
const metadataActivityId = getStringValue(playcademy?.activityId);
|
|
52110
|
+
if (metadataActivityId) {
|
|
52111
|
+
return metadataActivityId;
|
|
52112
|
+
}
|
|
52113
|
+
const activityId = getStringValue(event.object.activity?.id);
|
|
52114
|
+
if (activityId) {
|
|
52115
|
+
return activityId;
|
|
52116
|
+
}
|
|
52117
|
+
const objectId = getStringValue(event.object.id);
|
|
52118
|
+
if (!objectId) {
|
|
52119
|
+
return;
|
|
52120
|
+
}
|
|
52121
|
+
const trimmed = objectId.replace(/\/$/, "");
|
|
52122
|
+
const segments = trimmed.split("/");
|
|
52123
|
+
const activityIndex = segments.lastIndexOf("activities");
|
|
52124
|
+
if (activityIndex !== -1 && segments.length >= activityIndex + 3) {
|
|
52125
|
+
const candidate = segments[activityIndex + 2];
|
|
52126
|
+
return candidate ? decodeURIComponent(candidate) : undefined;
|
|
52127
|
+
}
|
|
52128
|
+
return;
|
|
52129
|
+
}
|
|
52130
|
+
function buildResourceMetadata({
|
|
52131
|
+
baseMetadata,
|
|
52132
|
+
subject,
|
|
52133
|
+
grade,
|
|
52134
|
+
totalXp,
|
|
52135
|
+
masterableUnits
|
|
52136
|
+
}) {
|
|
52137
|
+
const normalizedBaseMetadata = isResourceMetadata(baseMetadata) ? baseMetadata : undefined;
|
|
52138
|
+
const metadata2 = {
|
|
52139
|
+
...normalizedBaseMetadata
|
|
52140
|
+
};
|
|
52141
|
+
metadata2.subject = subject;
|
|
52142
|
+
metadata2.grades = [grade];
|
|
52143
|
+
metadata2.xp = totalXp;
|
|
52144
|
+
if (masterableUnits !== undefined && masterableUnits !== null) {
|
|
52145
|
+
const existingPlaycademy = isPlaycademyResourceMetadata(metadata2.playcademy) ? metadata2.playcademy : undefined;
|
|
52146
|
+
metadata2.playcademy = {
|
|
52147
|
+
...existingPlaycademy,
|
|
52148
|
+
mastery: {
|
|
52149
|
+
...existingPlaycademy?.mastery,
|
|
52150
|
+
masterableUnits
|
|
52151
|
+
}
|
|
52152
|
+
};
|
|
52153
|
+
}
|
|
52154
|
+
return metadata2;
|
|
52155
|
+
}
|
|
52156
|
+
function getDurationSecondsFromExtensions(event) {
|
|
52157
|
+
const extensions = getMergedCaliperExtensions(event);
|
|
52158
|
+
const playcademy = isRecord2(extensions.playcademy) ? extensions.playcademy : undefined;
|
|
52159
|
+
const rawValue = extensions.durationSeconds ?? playcademy?.durationSeconds;
|
|
52160
|
+
const value = typeof rawValue === "number" ? rawValue : Number(rawValue);
|
|
52161
|
+
return Number.isFinite(value) ? value : undefined;
|
|
52162
|
+
}
|
|
52163
|
+
function getCanonicalRunId(session2) {
|
|
52164
|
+
const sessionId = getStringValue(session2?.id);
|
|
52165
|
+
if (!sessionId) {
|
|
52166
|
+
return;
|
|
52167
|
+
}
|
|
52168
|
+
return sessionId.replace(/^urn:uuid:/, "");
|
|
52169
|
+
}
|
|
52170
|
+
function getResumeId(event) {
|
|
52171
|
+
const playcademy = getPlaycademyMetadata(event);
|
|
52172
|
+
return getStringValue(playcademy?.resumeId);
|
|
52173
|
+
}
|
|
52174
|
+
function isCaliperRemediationOrCompletionEvent(event) {
|
|
52175
|
+
const playcademy = getPlaycademyMetadata(event);
|
|
52176
|
+
return REMEDIATION_OR_COMPLETION_EVENT_KINDS.has(getStringValue(playcademy?.eventKind) || "");
|
|
52177
|
+
}
|
|
52178
|
+
function groupCaliperEventsByRun(events) {
|
|
52179
|
+
const groups = new Map;
|
|
52180
|
+
for (const event of events) {
|
|
52181
|
+
const objectId = getStringValue(event.object.id) || "unknown-activity";
|
|
52182
|
+
const groupKey = `${objectId}::${getStringValue(event.session?.id) || event.externalId}`;
|
|
52183
|
+
const existing = groups.get(groupKey);
|
|
52184
|
+
if (existing) {
|
|
52185
|
+
existing.push(event);
|
|
52186
|
+
} else {
|
|
52187
|
+
groups.set(groupKey, [event]);
|
|
52188
|
+
}
|
|
52189
|
+
}
|
|
52190
|
+
return groups;
|
|
52191
|
+
}
|
|
52192
|
+
function findCaliperEventGroupContainingExternalId(events, externalId) {
|
|
52193
|
+
const targetExternalId = externalId.trim();
|
|
52194
|
+
if (!targetExternalId) {
|
|
52195
|
+
return;
|
|
52196
|
+
}
|
|
52197
|
+
return [...groupCaliperEventsByRun(events).values()].find((group) => group.some((event) => event.externalId === targetExternalId));
|
|
52198
|
+
}
|
|
52199
|
+
function mapCaliperEventGroupToActivity(events, relevantCourseIds) {
|
|
52200
|
+
if (events.length === 0) {
|
|
52201
|
+
return null;
|
|
52202
|
+
}
|
|
52203
|
+
const sortedEvents = events.toSorted((a, b) => a.eventTime.localeCompare(b.eventTime));
|
|
52204
|
+
const activityEvent = [...sortedEvents].toReversed().find((event) => event.type === "ActivityEvent");
|
|
52205
|
+
const contextSource = activityEvent || sortedEvents.at(-1);
|
|
52206
|
+
if (!contextSource) {
|
|
52207
|
+
return null;
|
|
52208
|
+
}
|
|
52209
|
+
const ctx = parseCaliperEventContext(contextSource, relevantCourseIds);
|
|
52210
|
+
if (!ctx) {
|
|
52211
|
+
return null;
|
|
52212
|
+
}
|
|
52213
|
+
const score = activityEvent !== undefined ? (() => {
|
|
52214
|
+
const totalQuestions = getGeneratedMetricValue(activityEvent, "totalQuestions");
|
|
52215
|
+
const correctQuestions = getGeneratedMetricValue(activityEvent, "correctQuestions");
|
|
52216
|
+
if (totalQuestions === undefined || correctQuestions === undefined || totalQuestions <= 0) {
|
|
52217
|
+
return;
|
|
52218
|
+
}
|
|
52219
|
+
return correctQuestions / totalQuestions * 100;
|
|
52220
|
+
})() : undefined;
|
|
52221
|
+
const xpEarned = activityEvent !== undefined ? getGeneratedMetricValue(activityEvent, "xpEarned") : undefined;
|
|
52222
|
+
const masteredUnits = activityEvent !== undefined ? getGeneratedMetricValue(activityEvent, "masteredUnits") : undefined;
|
|
52223
|
+
const timeSpentEvents = sortedEvents.filter((event) => event.type === "TimeSpentEvent");
|
|
52224
|
+
let totalActiveTimeSeconds;
|
|
52225
|
+
if (timeSpentEvents.length > 0) {
|
|
52226
|
+
totalActiveTimeSeconds = timeSpentEvents.reduce((sum, event) => sum + (getGeneratedMetricValue(event, "active") ?? 0), 0);
|
|
52227
|
+
} else if (activityEvent !== undefined) {
|
|
52228
|
+
totalActiveTimeSeconds = getDurationSecondsFromExtensions(activityEvent);
|
|
52229
|
+
}
|
|
52230
|
+
const fallbackActivityId = getActivityId(contextSource, getPlaycademyMetadata(contextSource));
|
|
52231
|
+
const occurredAt = getStringValue(activityEvent?.eventTime) || getStringValue(sortedEvents.at(-1)?.eventTime);
|
|
52232
|
+
const runId = getCanonicalRunId(contextSource.session);
|
|
52233
|
+
const resumeIds = new Set(sortedEvents.map((event) => getResumeId(event)).filter((resumeId) => resumeId !== undefined));
|
|
52234
|
+
const sessionCount = resumeIds.size > 0 ? resumeIds.size : 1;
|
|
52235
|
+
const kind = activityEvent !== undefined ? "activity" : "activity-in-progress";
|
|
52236
|
+
if (!occurredAt) {
|
|
52237
|
+
return null;
|
|
52238
|
+
}
|
|
52239
|
+
return {
|
|
52240
|
+
id: activityEvent?.externalId || sortedEvents.at(-1)?.externalId || events[0].externalId,
|
|
52241
|
+
kind,
|
|
52242
|
+
occurredAt,
|
|
52243
|
+
courseId: ctx.courseId,
|
|
52244
|
+
title: getStringValue(activityEvent?.object.activity?.name) || ctx.titleFromEvent || (fallbackActivityId ? kebabToTitleCase(fallbackActivityId) : "Activity completed"),
|
|
52245
|
+
...ctx.activityId ? { activityId: ctx.activityId } : {},
|
|
52246
|
+
...ctx.appName ? { appName: ctx.appName } : {},
|
|
52247
|
+
...score !== undefined ? { score } : {},
|
|
52248
|
+
...xpEarned !== undefined ? { xpDelta: xpEarned } : {},
|
|
52249
|
+
...masteredUnits !== undefined ? { masteredUnitsDelta: masteredUnits } : {},
|
|
52250
|
+
...totalActiveTimeSeconds !== undefined ? { timeDeltaSeconds: totalActiveTimeSeconds } : {},
|
|
52251
|
+
...runId ? { runId } : {},
|
|
52252
|
+
...sessionCount > 0 ? { sessionCount } : {}
|
|
52253
|
+
};
|
|
52254
|
+
}
|
|
52255
|
+
function parseCaliperEventContext(event, relevantCourseIds) {
|
|
52256
|
+
const playcademy = getPlaycademyMetadata(event);
|
|
52257
|
+
const courseId = getStringValue(playcademy?.courseId) || parseSourcedIdFromUrl(event.object.course?.id);
|
|
52258
|
+
if (!courseId || !relevantCourseIds.has(courseId)) {
|
|
52259
|
+
return null;
|
|
52260
|
+
}
|
|
52261
|
+
const occurredAt = getStringValue(event.eventTime);
|
|
52262
|
+
if (!occurredAt) {
|
|
52263
|
+
return null;
|
|
52264
|
+
}
|
|
52265
|
+
return {
|
|
52266
|
+
courseId,
|
|
52267
|
+
occurredAt,
|
|
52268
|
+
eventKind: getStringValue(playcademy?.eventKind),
|
|
52269
|
+
source: getStringValue(playcademy?.source),
|
|
52270
|
+
reason: getStringValue(playcademy?.reason),
|
|
52271
|
+
titleFromEvent: getStringValue(event.object.activity?.name),
|
|
52272
|
+
appName: getStringValue(event.object.app?.name),
|
|
52273
|
+
activityId: getActivityId(event, playcademy)
|
|
52274
|
+
};
|
|
52275
|
+
}
|
|
52276
|
+
function mapTimeSpentRemediation(event, ctx) {
|
|
52277
|
+
if (ctx.eventKind !== "remediation-time") {
|
|
52278
|
+
return null;
|
|
52279
|
+
}
|
|
52280
|
+
return {
|
|
52281
|
+
id: event.externalId,
|
|
52282
|
+
kind: "remediation-time",
|
|
52283
|
+
occurredAt: ctx.occurredAt,
|
|
52284
|
+
courseId: ctx.courseId,
|
|
52285
|
+
title: "Time Adjustment",
|
|
52286
|
+
activityId: ctx.activityId,
|
|
52287
|
+
appName: ctx.appName,
|
|
52288
|
+
reason: ctx.reason,
|
|
52289
|
+
timeDeltaSeconds: getGeneratedMetricValue(event, "active")
|
|
52290
|
+
};
|
|
52291
|
+
}
|
|
52292
|
+
function mapActivityRemediation(event, ctx) {
|
|
52293
|
+
if (ctx.eventKind === "remediation-xp") {
|
|
52294
|
+
return {
|
|
52295
|
+
id: event.externalId,
|
|
52296
|
+
kind: "remediation-xp",
|
|
52297
|
+
occurredAt: ctx.occurredAt,
|
|
52298
|
+
courseId: ctx.courseId,
|
|
52299
|
+
title: "XP Adjustment",
|
|
52300
|
+
activityId: ctx.activityId,
|
|
52301
|
+
appName: ctx.appName,
|
|
52302
|
+
reason: ctx.reason,
|
|
52303
|
+
xpDelta: getGeneratedMetricValue(event, "xpEarned"),
|
|
52304
|
+
masteredUnitsDelta: getGeneratedMetricValue(event, "masteredUnits")
|
|
52305
|
+
};
|
|
52306
|
+
}
|
|
52307
|
+
if (ctx.eventKind === "remediation-mastery") {
|
|
52308
|
+
return {
|
|
52309
|
+
id: event.externalId,
|
|
52310
|
+
kind: "remediation-mastery",
|
|
52311
|
+
occurredAt: ctx.occurredAt,
|
|
52312
|
+
courseId: ctx.courseId,
|
|
52313
|
+
title: "Mastery Adjustment",
|
|
52314
|
+
activityId: ctx.activityId,
|
|
52315
|
+
appName: ctx.appName,
|
|
52316
|
+
reason: ctx.reason,
|
|
52317
|
+
xpDelta: getGeneratedMetricValue(event, "xpEarned"),
|
|
52318
|
+
masteredUnitsDelta: getGeneratedMetricValue(event, "masteredUnits")
|
|
52319
|
+
};
|
|
52320
|
+
}
|
|
52321
|
+
if (ctx.eventKind === "course-completed") {
|
|
52322
|
+
return {
|
|
52323
|
+
id: event.externalId,
|
|
52324
|
+
kind: "course-completed",
|
|
52325
|
+
occurredAt: ctx.occurredAt,
|
|
52326
|
+
courseId: ctx.courseId,
|
|
52327
|
+
title: ctx.source === "admin" ? "Course marked complete" : "Course completed",
|
|
52328
|
+
activityId: ctx.activityId,
|
|
52329
|
+
appName: ctx.appName,
|
|
52330
|
+
reason: ctx.reason
|
|
52331
|
+
};
|
|
52332
|
+
}
|
|
52333
|
+
if (ctx.eventKind === "course-resumed") {
|
|
52334
|
+
return {
|
|
52335
|
+
id: event.externalId,
|
|
52336
|
+
kind: "course-resumed",
|
|
52337
|
+
occurredAt: ctx.occurredAt,
|
|
52338
|
+
courseId: ctx.courseId,
|
|
52339
|
+
title: "Course resumed",
|
|
52340
|
+
activityId: ctx.activityId,
|
|
52341
|
+
appName: ctx.appName,
|
|
52342
|
+
reason: ctx.reason
|
|
52343
|
+
};
|
|
52344
|
+
}
|
|
52345
|
+
return null;
|
|
52346
|
+
}
|
|
52347
|
+
function mapCaliperEventToRemediationActivity(event, relevantCourseIds) {
|
|
52348
|
+
const ctx = parseCaliperEventContext(event, relevantCourseIds);
|
|
52349
|
+
if (!ctx) {
|
|
52350
|
+
return null;
|
|
52351
|
+
}
|
|
52352
|
+
if (event.type === "TimeSpentEvent") {
|
|
52353
|
+
return mapTimeSpentRemediation(event, ctx);
|
|
52354
|
+
}
|
|
52355
|
+
if (event.type === "ActivityEvent") {
|
|
52356
|
+
return mapActivityRemediation(event, ctx);
|
|
52357
|
+
}
|
|
52358
|
+
return null;
|
|
52359
|
+
}
|
|
52360
|
+
var REMEDIATION_OR_COMPLETION_EVENT_KINDS;
|
|
52361
|
+
var init_timeback_util = __esm(() => {
|
|
52362
|
+
init_helpers_index();
|
|
52363
|
+
init_types2();
|
|
52364
|
+
REMEDIATION_OR_COMPLETION_EVENT_KINDS = new Set([
|
|
52365
|
+
"remediation-xp",
|
|
52366
|
+
"remediation-time",
|
|
52367
|
+
"remediation-mastery",
|
|
52368
|
+
"course-completed",
|
|
52369
|
+
"course-resumed"
|
|
52370
|
+
]);
|
|
52371
|
+
});
|
|
51757
52372
|
var inFlightManifestFetches;
|
|
51758
52373
|
var GameService;
|
|
51759
52374
|
var init_game_service = __esm(() => {
|
|
51760
52375
|
init_drizzle_orm();
|
|
51761
52376
|
init_src();
|
|
52377
|
+
init_helpers_index();
|
|
51762
52378
|
init_tables_index();
|
|
51763
52379
|
init_spans();
|
|
51764
52380
|
init_errors();
|
|
51765
52381
|
init_deployment_util();
|
|
52382
|
+
init_timeback_util();
|
|
51766
52383
|
inFlightManifestFetches = new Map;
|
|
51767
52384
|
GameService = class GameService2 {
|
|
51768
52385
|
deps;
|
|
@@ -51906,6 +52523,7 @@ var init_game_service = __esm(() => {
|
|
|
51906
52523
|
const db2 = this.deps.db;
|
|
51907
52524
|
const integrations = await db2.query.gameTimebackIntegrations.findMany({
|
|
51908
52525
|
columns: { gameId: true, subject: true },
|
|
52526
|
+
where: isActiveGameTimebackIntegrationStatus(),
|
|
51909
52527
|
orderBy: [asc(gameTimebackIntegrations.createdAt)]
|
|
51910
52528
|
});
|
|
51911
52529
|
const subjectMap = {};
|
|
@@ -51916,6 +52534,26 @@ var init_game_service = __esm(() => {
|
|
|
51916
52534
|
}
|
|
51917
52535
|
return subjectMap;
|
|
51918
52536
|
}
|
|
52537
|
+
async getTimebackSummaries(user) {
|
|
52538
|
+
const db2 = this.deps.db;
|
|
52539
|
+
const canSeeAllGames = user.role === "admin" || user.role === "teacher";
|
|
52540
|
+
let accessibleGameIds = null;
|
|
52541
|
+
if (!canSeeAllGames) {
|
|
52542
|
+
const accessibleGames = await this.listAccessible(user);
|
|
52543
|
+
accessibleGameIds = new Set(accessibleGames.map((game2) => game2.id));
|
|
52544
|
+
}
|
|
52545
|
+
if (accessibleGameIds?.size === 0) {
|
|
52546
|
+
return {};
|
|
52547
|
+
}
|
|
52548
|
+
const integrations = await db2.query.gameTimebackIntegrations.findMany({
|
|
52549
|
+
columns: { gameId: true, subject: true, status: true },
|
|
52550
|
+
...accessibleGameIds && {
|
|
52551
|
+
where: inArray(gameTimebackIntegrations.gameId, [...accessibleGameIds])
|
|
52552
|
+
},
|
|
52553
|
+
orderBy: [asc(gameTimebackIntegrations.createdAt)]
|
|
52554
|
+
});
|
|
52555
|
+
return buildGameTimebackSummaries(integrations);
|
|
52556
|
+
}
|
|
51919
52557
|
async getById(gameId, caller) {
|
|
51920
52558
|
const db2 = this.deps.db;
|
|
51921
52559
|
const game2 = await db2.query.games.findFirst({
|
|
@@ -52545,7 +53183,7 @@ class DiscordEmbedBuilder {
|
|
|
52545
53183
|
}
|
|
52546
53184
|
var init_client2 = () => {};
|
|
52547
53185
|
var DiscordColors;
|
|
52548
|
-
var
|
|
53186
|
+
var init_types3 = __esm(() => {
|
|
52549
53187
|
DiscordColors = {
|
|
52550
53188
|
DEFAULT: 0,
|
|
52551
53189
|
WHITE: 16777215,
|
|
@@ -52581,7 +53219,7 @@ var init_types2 = __esm(() => {
|
|
|
52581
53219
|
});
|
|
52582
53220
|
var init_discord = __esm(() => {
|
|
52583
53221
|
init_client2();
|
|
52584
|
-
|
|
53222
|
+
init_types3();
|
|
52585
53223
|
});
|
|
52586
53224
|
function truncateField(value, maxLength = DISCORD_FIELD_LIMIT) {
|
|
52587
53225
|
if (value.length <= maxLength) {
|
|
@@ -53702,7 +54340,7 @@ var init_secrets_service = __esm(() => {
|
|
|
53702
54340
|
});
|
|
53703
54341
|
var ASSET_ROUTE_PREFIX = "/api/assets/";
|
|
53704
54342
|
var ROUTES;
|
|
53705
|
-
var
|
|
54343
|
+
var init_constants4 = __esm(() => {
|
|
53706
54344
|
init_src();
|
|
53707
54345
|
ROUTES = {
|
|
53708
54346
|
INDEX: "/api",
|
|
@@ -53728,7 +54366,7 @@ function prefixSecrets(secrets) {
|
|
|
53728
54366
|
}
|
|
53729
54367
|
var init_setup2 = __esm(() => {
|
|
53730
54368
|
init_src();
|
|
53731
|
-
|
|
54369
|
+
init_constants4();
|
|
53732
54370
|
});
|
|
53733
54371
|
|
|
53734
54372
|
class SeedService {
|
|
@@ -54469,11 +55107,11 @@ var init_schemas3 = __esm(() => {
|
|
|
54469
55107
|
metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
|
|
54470
55108
|
});
|
|
54471
55109
|
});
|
|
54472
|
-
function
|
|
55110
|
+
function isTimebackGrade2(value) {
|
|
54473
55111
|
return Number.isInteger(value) && TIMEBACK_GRADES.includes(value);
|
|
54474
55112
|
}
|
|
54475
|
-
function
|
|
54476
|
-
return
|
|
55113
|
+
function isTimebackSubject2(value) {
|
|
55114
|
+
return TIMEBACK_SUBJECTS2.includes(value);
|
|
54477
55115
|
}
|
|
54478
55116
|
function isValidAdminAttributionDate(value) {
|
|
54479
55117
|
const match = value.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
|
@@ -54488,11 +55126,12 @@ function isValidAdminAttributionDate(value) {
|
|
|
54488
55126
|
return date3.getUTCFullYear() === year && date3.getUTCMonth() + 1 === month && date3.getUTCDate() === day;
|
|
54489
55127
|
}
|
|
54490
55128
|
var TIMEBACK_GRADES;
|
|
54491
|
-
var
|
|
55129
|
+
var TIMEBACK_SUBJECTS2;
|
|
54492
55130
|
var TimebackGradeSchema;
|
|
54493
55131
|
var TimebackSubjectSchema;
|
|
54494
55132
|
var CourseGoalsSchema;
|
|
54495
55133
|
var UpdateGameTimebackIntegrationRequestSchema;
|
|
55134
|
+
var CreateGameTimebackIntegrationRequestSchema;
|
|
54496
55135
|
var TimebackActivityDataSchema;
|
|
54497
55136
|
var EndActivityRequestSchema;
|
|
54498
55137
|
var GameRunMetricsSchema;
|
|
@@ -54527,7 +55166,7 @@ var init_schemas4 = __esm(() => {
|
|
|
54527
55166
|
init_esm();
|
|
54528
55167
|
init_table7();
|
|
54529
55168
|
TIMEBACK_GRADES = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
|
|
54530
|
-
|
|
55169
|
+
TIMEBACK_SUBJECTS2 = [
|
|
54531
55170
|
"Reading",
|
|
54532
55171
|
"Language",
|
|
54533
55172
|
"Vocabulary",
|
|
@@ -54541,7 +55180,7 @@ var init_schemas4 = __esm(() => {
|
|
|
54541
55180
|
TimebackGradeSchema = exports_external.number().int().refine((val) => TIMEBACK_GRADES.includes(val), {
|
|
54542
55181
|
message: `Grade must be one of: ${TIMEBACK_GRADES.join(", ")}`
|
|
54543
55182
|
});
|
|
54544
|
-
TimebackSubjectSchema = exports_external.enum(
|
|
55183
|
+
TimebackSubjectSchema = exports_external.enum(TIMEBACK_SUBJECTS2);
|
|
54545
55184
|
CourseGoalsSchema = exports_external.object({
|
|
54546
55185
|
dailyXp: exports_external.number().int().nonnegative().nullable().optional(),
|
|
54547
55186
|
dailyLessons: exports_external.number().int().nonnegative().nullable().optional(),
|
|
@@ -54560,6 +55199,15 @@ var init_schemas4 = __esm(() => {
|
|
|
54560
55199
|
isSupplemental: exports_external.boolean().optional(),
|
|
54561
55200
|
timebackVisible: exports_external.boolean().nullable().optional()
|
|
54562
55201
|
});
|
|
55202
|
+
CreateGameTimebackIntegrationRequestSchema = exports_external.object({
|
|
55203
|
+
title: exports_external.string().trim().min(1),
|
|
55204
|
+
courseCode: exports_external.string().trim().min(1),
|
|
55205
|
+
subject: TimebackSubjectSchema,
|
|
55206
|
+
grade: TimebackGradeSchema,
|
|
55207
|
+
totalXp: exports_external.number().int().positive(),
|
|
55208
|
+
masterableUnits: exports_external.number().int().nonnegative(),
|
|
55209
|
+
level: exports_external.string().trim().min(1).optional()
|
|
55210
|
+
});
|
|
54563
55211
|
TimebackActivityDataSchema = exports_external.object({
|
|
54564
55212
|
activityId: exports_external.string().min(1),
|
|
54565
55213
|
activityName: exports_external.string().optional(),
|
|
@@ -55001,9 +55649,6 @@ var init_log = __esm(() => {
|
|
|
55001
55649
|
init_ansi();
|
|
55002
55650
|
init_spinner();
|
|
55003
55651
|
});
|
|
55004
|
-
function kebabToTitleCase(kebabStr) {
|
|
55005
|
-
return kebabStr.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
55006
|
-
}
|
|
55007
55652
|
function formatDateYMDInTimezone(timeZone, date3 = new Date) {
|
|
55008
55653
|
const parts2 = new Intl.DateTimeFormat("en-US", {
|
|
55009
55654
|
timeZone,
|
|
@@ -55106,14 +55751,42 @@ var init_src4 = __esm(() => {
|
|
|
55106
55751
|
init_pure();
|
|
55107
55752
|
});
|
|
55108
55753
|
function createOneRosterUrls(baseUrl) {
|
|
55109
|
-
const effective = baseUrl ||
|
|
55754
|
+
const effective = baseUrl || TIMEBACK_API_URLS2.production;
|
|
55110
55755
|
const base = effective.replace(/\/$/, "");
|
|
55111
55756
|
return {
|
|
55112
|
-
user: (userId) => `${base}${
|
|
55113
|
-
course: (courseId) => `${base}${
|
|
55114
|
-
componentResource: (resourceId) => `${base}${
|
|
55757
|
+
user: (userId) => `${base}${ONEROSTER_ENDPOINTS2.users}/${userId}`,
|
|
55758
|
+
course: (courseId) => `${base}${ONEROSTER_ENDPOINTS2.courses}/${courseId}`,
|
|
55759
|
+
componentResource: (resourceId) => `${base}${ONEROSTER_ENDPOINTS2.componentResources}/${resourceId}`
|
|
55115
55760
|
};
|
|
55116
55761
|
}
|
|
55762
|
+
function resolveCourseIdFromCourseUrl(url2) {
|
|
55763
|
+
try {
|
|
55764
|
+
const parts2 = new URL(url2).pathname.split("/").filter(Boolean);
|
|
55765
|
+
const maybeCourses = parts2.at(-2);
|
|
55766
|
+
const maybeId = parts2.at(-1);
|
|
55767
|
+
if (maybeCourses !== "courses" || !maybeId) {
|
|
55768
|
+
return null;
|
|
55769
|
+
}
|
|
55770
|
+
if (UUID_REGEX2.test(maybeId)) {
|
|
55771
|
+
return maybeId;
|
|
55772
|
+
}
|
|
55773
|
+
if (maybeId.length > 2 && !maybeId.includes("/")) {
|
|
55774
|
+
return maybeId;
|
|
55775
|
+
}
|
|
55776
|
+
return null;
|
|
55777
|
+
} catch {
|
|
55778
|
+
return null;
|
|
55779
|
+
}
|
|
55780
|
+
}
|
|
55781
|
+
function computeCaliperLineItemId(objectId, courseSourcedId, courseUrl) {
|
|
55782
|
+
const recovered = resolveCourseIdFromCourseUrl(courseUrl);
|
|
55783
|
+
if (recovered !== courseSourcedId) {
|
|
55784
|
+
throw new ConfigurationError("courseId", `Course id "${courseSourcedId}" is not URL-safe: Timeback Platform would parse it back as ${recovered === null ? "null" : `"${recovered}"`} from the Caliper event, producing a different gradebook line item. Use a UUID or an id with no spaces, slashes, or non-ASCII characters.`);
|
|
55785
|
+
}
|
|
55786
|
+
const idParts = [objectId, courseSourcedId].join("_");
|
|
55787
|
+
const hashedId = createHash("sha256").update(idParts).digest("hex");
|
|
55788
|
+
return `caliper_${hashedId}`;
|
|
55789
|
+
}
|
|
55117
55790
|
function deriveSourcedIds(courseId) {
|
|
55118
55791
|
return {
|
|
55119
55792
|
course: courseId,
|
|
@@ -55125,7 +55798,7 @@ function deriveSourcedIds(courseId) {
|
|
|
55125
55798
|
async function fetchTimebackConfig(client, courseId) {
|
|
55126
55799
|
const sourcedIds = deriveSourcedIds(courseId);
|
|
55127
55800
|
const [org, course, component, resource, componentResource] = await Promise.all([
|
|
55128
|
-
client.oneroster.organizations.get(
|
|
55801
|
+
client.oneroster.organizations.get(PLAYCADEMY_DEFAULTS2.organization),
|
|
55129
55802
|
client.oneroster.courses.get(sourcedIds.course),
|
|
55130
55803
|
client.oneroster.courseComponents.get(sourcedIds.component),
|
|
55131
55804
|
client.oneroster.resources.get(sourcedIds.resource),
|
|
@@ -55135,7 +55808,7 @@ async function fetchTimebackConfig(client, courseId) {
|
|
|
55135
55808
|
organization: {
|
|
55136
55809
|
name: org.name,
|
|
55137
55810
|
type: org.type,
|
|
55138
|
-
identifier: org.identifier ||
|
|
55811
|
+
identifier: org.identifier || PLAYCADEMY_DEFAULTS2.organization
|
|
55139
55812
|
},
|
|
55140
55813
|
course: {
|
|
55141
55814
|
title: course.title || "",
|
|
@@ -55195,23 +55868,23 @@ async function verifyTimebackResources(client, courseId) {
|
|
|
55195
55868
|
}
|
|
55196
55869
|
};
|
|
55197
55870
|
}
|
|
55198
|
-
function
|
|
55871
|
+
function isObject2(value) {
|
|
55199
55872
|
return typeof value === "object" && value !== null;
|
|
55200
55873
|
}
|
|
55201
|
-
function
|
|
55202
|
-
if (!
|
|
55874
|
+
function isPlaycademyResourceMetadata2(value) {
|
|
55875
|
+
if (!isObject2(value)) {
|
|
55203
55876
|
return false;
|
|
55204
55877
|
}
|
|
55205
55878
|
if (!("mastery" in value) || value.mastery === undefined) {
|
|
55206
55879
|
return true;
|
|
55207
55880
|
}
|
|
55208
|
-
return
|
|
55881
|
+
return isObject2(value.mastery);
|
|
55209
55882
|
}
|
|
55210
|
-
function
|
|
55211
|
-
return typeof value === "string" &&
|
|
55883
|
+
function isTimebackSubject3(value) {
|
|
55884
|
+
return typeof value === "string" && SUBJECT_VALUES2.includes(value);
|
|
55212
55885
|
}
|
|
55213
|
-
function
|
|
55214
|
-
return typeof value === "number" && Number.isInteger(value) &&
|
|
55886
|
+
function isTimebackGrade3(value) {
|
|
55887
|
+
return typeof value === "number" && Number.isInteger(value) && GRADE_VALUES2.includes(value);
|
|
55215
55888
|
}
|
|
55216
55889
|
async function deleteTimebackResources(client, courseId) {
|
|
55217
55890
|
const sourcedIds = deriveSourcedIds(courseId);
|
|
@@ -55262,6 +55935,15 @@ async function deleteTimebackResources(client, courseId) {
|
|
|
55262
55935
|
]);
|
|
55263
55936
|
setAttribute("app.timeback.resources_deleted", true);
|
|
55264
55937
|
}
|
|
55938
|
+
async function updateTimebackCourseStatus(client, courseId, status) {
|
|
55939
|
+
const course = await client.oneroster.courses.get(courseId);
|
|
55940
|
+
await client.oneroster.courses.update(courseId, {
|
|
55941
|
+
...course,
|
|
55942
|
+
sourcedId: courseId,
|
|
55943
|
+
status
|
|
55944
|
+
});
|
|
55945
|
+
setAttribute("app.timeback.course_status", status);
|
|
55946
|
+
}
|
|
55265
55947
|
async function createCourse(client, config2) {
|
|
55266
55948
|
const courseData = {
|
|
55267
55949
|
status: "active",
|
|
@@ -55510,14 +56192,14 @@ async function getTimebackTokenResponse(config2) {
|
|
|
55510
56192
|
}
|
|
55511
56193
|
}
|
|
55512
56194
|
function getAuthUrl(environment = "production") {
|
|
55513
|
-
return
|
|
56195
|
+
return TIMEBACK_AUTH_URLS2[environment];
|
|
55514
56196
|
}
|
|
55515
56197
|
function parseEduBridgeGrade(value) {
|
|
55516
56198
|
if (value === null || value === undefined || value.trim() === "") {
|
|
55517
56199
|
return null;
|
|
55518
56200
|
}
|
|
55519
56201
|
const parsed = Number(value);
|
|
55520
|
-
return
|
|
56202
|
+
return isTimebackGrade3(parsed) ? parsed : null;
|
|
55521
56203
|
}
|
|
55522
56204
|
function normalizeHighestGradeMastered(response, subject) {
|
|
55523
56205
|
const grades = {
|
|
@@ -55594,12 +56276,12 @@ async function withTimebackClientTelemetry(fn) {
|
|
|
55594
56276
|
}
|
|
55595
56277
|
function handleHttpError(res, errorBody, attempt, retries, context2) {
|
|
55596
56278
|
const error = new TimebackApiError(res.status, res.statusText, errorBody);
|
|
55597
|
-
if (res.status >=
|
|
56279
|
+
if (res.status >= HTTP_STATUS2.CLIENT_ERROR_MIN && res.status < HTTP_STATUS2.CLIENT_ERROR_MAX) {
|
|
55598
56280
|
recordTimebackHttpFailure();
|
|
55599
56281
|
throw error;
|
|
55600
56282
|
}
|
|
55601
56283
|
if (attempt < retries) {
|
|
55602
|
-
const delay =
|
|
56284
|
+
const delay = HTTP_DEFAULTS2.retryBackoffBase ** attempt * 1000;
|
|
55603
56285
|
recordTimebackRetry();
|
|
55604
56286
|
addEvent("timeback.request_retry", {
|
|
55605
56287
|
"app.timeback.attempt": attempt + 1,
|
|
@@ -55676,7 +56358,7 @@ async function request({
|
|
|
55676
56358
|
const result = handleHttpError(res, errorBody, attempt, retries, { method, path: path2 });
|
|
55677
56359
|
lastError = result.error;
|
|
55678
56360
|
if (result.retry) {
|
|
55679
|
-
const delay =
|
|
56361
|
+
const delay = HTTP_DEFAULTS2.retryBackoffBase ** attempt * 1000;
|
|
55680
56362
|
await new Promise((resolve2) => setTimeout(resolve2, delay));
|
|
55681
56363
|
}
|
|
55682
56364
|
} else {
|
|
@@ -55688,7 +56370,7 @@ async function request({
|
|
|
55688
56370
|
}
|
|
55689
56371
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
55690
56372
|
if (attempt < retries) {
|
|
55691
|
-
const delay =
|
|
56373
|
+
const delay = HTTP_DEFAULTS2.retryBackoffBase ** attempt * 1000;
|
|
55692
56374
|
recordTimebackRetry();
|
|
55693
56375
|
addEvent("timeback.network_retry", {
|
|
55694
56376
|
"app.timeback.attempt": attempt + 1,
|
|
@@ -55737,10 +56419,10 @@ function createCaliperNamespace(client) {
|
|
|
55737
56419
|
const envelope = {
|
|
55738
56420
|
sensor: sensorUrl,
|
|
55739
56421
|
sendTime: new Date().toISOString(),
|
|
55740
|
-
dataVersion:
|
|
56422
|
+
dataVersion: CALIPER_CONSTANTS2.dataVersion,
|
|
55741
56423
|
data: [event]
|
|
55742
56424
|
};
|
|
55743
|
-
return client["requestCaliper"](
|
|
56425
|
+
return client["requestCaliper"](CALIPER_ENDPOINTS2.event, "POST", envelope);
|
|
55744
56426
|
},
|
|
55745
56427
|
emitBatch: async (events, sensorUrl) => {
|
|
55746
56428
|
if (events.length === 0) {
|
|
@@ -55749,10 +56431,10 @@ function createCaliperNamespace(client) {
|
|
|
55749
56431
|
const envelope = {
|
|
55750
56432
|
sensor: sensorUrl,
|
|
55751
56433
|
sendTime: new Date().toISOString(),
|
|
55752
|
-
dataVersion:
|
|
56434
|
+
dataVersion: CALIPER_CONSTANTS2.dataVersion,
|
|
55753
56435
|
data: events
|
|
55754
56436
|
};
|
|
55755
|
-
return client["requestCaliper"](
|
|
56437
|
+
return client["requestCaliper"](CALIPER_ENDPOINTS2.event, "POST", envelope);
|
|
55756
56438
|
},
|
|
55757
56439
|
events: {
|
|
55758
56440
|
list: async (params = {}) => {
|
|
@@ -55782,7 +56464,7 @@ function createCaliperNamespace(client) {
|
|
|
55782
56464
|
query.set(`extensions.${key}`, value);
|
|
55783
56465
|
}
|
|
55784
56466
|
}
|
|
55785
|
-
const requestPath = `${
|
|
56467
|
+
const requestPath = `${CALIPER_ENDPOINTS2.events}?${query.toString()}`;
|
|
55786
56468
|
return client["requestCaliper"](requestPath, "GET");
|
|
55787
56469
|
}
|
|
55788
56470
|
},
|
|
@@ -55792,21 +56474,21 @@ function createCaliperNamespace(client) {
|
|
|
55792
56474
|
gameId: data.gameId
|
|
55793
56475
|
});
|
|
55794
56476
|
const event = {
|
|
55795
|
-
"@context":
|
|
56477
|
+
"@context": CALIPER_CONSTANTS2.context,
|
|
55796
56478
|
id: `urn:uuid:${crypto.randomUUID()}`,
|
|
55797
|
-
type:
|
|
56479
|
+
type: TIMEBACK_EVENT_TYPES2.activityEvent,
|
|
55798
56480
|
eventTime: data.eventTime || new Date().toISOString(),
|
|
55799
|
-
profile:
|
|
56481
|
+
profile: CALIPER_CONSTANTS2.profile,
|
|
55800
56482
|
actor: {
|
|
55801
56483
|
id: urls.user(data.studentId),
|
|
55802
|
-
type:
|
|
56484
|
+
type: TIMEBACK_TYPES2.user,
|
|
55803
56485
|
email: data.studentEmail
|
|
55804
56486
|
},
|
|
55805
|
-
action:
|
|
56487
|
+
action: TIMEBACK_ACTIONS2.completed,
|
|
55806
56488
|
...data.runId ? { session: `urn:uuid:${data.runId}` } : {},
|
|
55807
56489
|
object: {
|
|
55808
56490
|
id: data.objectId || caliper.buildActivityUrl(data),
|
|
55809
|
-
type:
|
|
56491
|
+
type: TIMEBACK_TYPES2.activityContext,
|
|
55810
56492
|
subject: data.subject,
|
|
55811
56493
|
app: {
|
|
55812
56494
|
name: data.appName
|
|
@@ -55820,25 +56502,25 @@ function createCaliperNamespace(client) {
|
|
|
55820
56502
|
},
|
|
55821
56503
|
generated: {
|
|
55822
56504
|
id: data.generatedId || `urn:timeback:metrics:activity-completion-${crypto.randomUUID()}`,
|
|
55823
|
-
type:
|
|
56505
|
+
type: TIMEBACK_TYPES2.activityMetricsCollection,
|
|
55824
56506
|
...data.includeAttempt === false ? {} : { attempt: data.attemptNumber || 1 },
|
|
55825
56507
|
items: [
|
|
55826
56508
|
...data.totalQuestions !== undefined ? [
|
|
55827
56509
|
{
|
|
55828
|
-
type:
|
|
56510
|
+
type: ACTIVITY_METRIC_TYPES2.totalQuestions,
|
|
55829
56511
|
value: data.totalQuestions
|
|
55830
56512
|
}
|
|
55831
56513
|
] : [],
|
|
55832
56514
|
...data.correctQuestions !== undefined ? [
|
|
55833
56515
|
{
|
|
55834
|
-
type:
|
|
56516
|
+
type: ACTIVITY_METRIC_TYPES2.correctQuestions,
|
|
55835
56517
|
value: data.correctQuestions
|
|
55836
56518
|
}
|
|
55837
56519
|
] : [],
|
|
55838
|
-
...data.xpEarned !== undefined ? [{ type:
|
|
56520
|
+
...data.xpEarned !== undefined ? [{ type: ACTIVITY_METRIC_TYPES2.xpEarned, value: data.xpEarned }] : [],
|
|
55839
56521
|
...data.masteredUnits !== undefined ? [
|
|
55840
56522
|
{
|
|
55841
|
-
type:
|
|
56523
|
+
type: ACTIVITY_METRIC_TYPES2.masteredUnits,
|
|
55842
56524
|
value: data.masteredUnits
|
|
55843
56525
|
}
|
|
55844
56526
|
] : []
|
|
@@ -55860,21 +56542,21 @@ function createCaliperNamespace(client) {
|
|
|
55860
56542
|
gameId: data.gameId
|
|
55861
56543
|
});
|
|
55862
56544
|
const event = {
|
|
55863
|
-
"@context":
|
|
56545
|
+
"@context": CALIPER_CONSTANTS2.context,
|
|
55864
56546
|
id: `urn:uuid:${crypto.randomUUID()}`,
|
|
55865
|
-
type:
|
|
56547
|
+
type: TIMEBACK_EVENT_TYPES2.timeSpentEvent,
|
|
55866
56548
|
eventTime: data.eventTime || new Date().toISOString(),
|
|
55867
|
-
profile:
|
|
56549
|
+
profile: CALIPER_CONSTANTS2.profile,
|
|
55868
56550
|
actor: {
|
|
55869
56551
|
id: urls.user(data.studentId),
|
|
55870
|
-
type:
|
|
56552
|
+
type: TIMEBACK_TYPES2.user,
|
|
55871
56553
|
email: data.studentEmail
|
|
55872
56554
|
},
|
|
55873
|
-
action:
|
|
56555
|
+
action: TIMEBACK_ACTIONS2.spentTime,
|
|
55874
56556
|
...data.runId ? { session: `urn:uuid:${data.runId}` } : {},
|
|
55875
56557
|
object: {
|
|
55876
56558
|
id: caliper.buildActivityUrl(data),
|
|
55877
|
-
type:
|
|
56559
|
+
type: TIMEBACK_TYPES2.activityContext,
|
|
55878
56560
|
subject: data.subject,
|
|
55879
56561
|
app: {
|
|
55880
56562
|
name: data.appName
|
|
@@ -55887,16 +56569,16 @@ function createCaliperNamespace(client) {
|
|
|
55887
56569
|
},
|
|
55888
56570
|
generated: {
|
|
55889
56571
|
id: `urn:timeback:metrics:time-spent-${crypto.randomUUID()}`,
|
|
55890
|
-
type:
|
|
56572
|
+
type: TIMEBACK_TYPES2.timeSpentMetricsCollection,
|
|
55891
56573
|
items: [
|
|
55892
|
-
{ type:
|
|
56574
|
+
{ type: TIME_METRIC_TYPES2.active, value: data.activeTimeSeconds },
|
|
55893
56575
|
...data.inactiveTimeSeconds !== undefined ? [
|
|
55894
56576
|
{
|
|
55895
|
-
type:
|
|
56577
|
+
type: TIME_METRIC_TYPES2.inactive,
|
|
55896
56578
|
value: data.inactiveTimeSeconds
|
|
55897
56579
|
}
|
|
55898
56580
|
] : [],
|
|
55899
|
-
...data.wasteTimeSeconds !== undefined ? [{ type:
|
|
56581
|
+
...data.wasteTimeSeconds !== undefined ? [{ type: TIME_METRIC_TYPES2.waste, value: data.wasteTimeSeconds }] : []
|
|
55900
56582
|
],
|
|
55901
56583
|
...data.extensions ? { extensions: data.extensions } : {}
|
|
55902
56584
|
},
|
|
@@ -55907,7 +56589,8 @@ function createCaliperNamespace(client) {
|
|
|
55907
56589
|
buildActivityUrl: (data) => {
|
|
55908
56590
|
const base = data.sensorUrl.replace(/\/$/, "");
|
|
55909
56591
|
return `${base}/activities/${encodeURIComponent(data.courseId)}/${encodeURIComponent(data.activityId)}`;
|
|
55910
|
-
}
|
|
56592
|
+
},
|
|
56593
|
+
buildCourseUrl: (courseId) => urls.course(courseId)
|
|
55911
56594
|
};
|
|
55912
56595
|
return caliper;
|
|
55913
56596
|
}
|
|
@@ -56030,17 +56713,17 @@ async function listOneRosterCollection(client, endpoint, collectionKey, options)
|
|
|
56030
56713
|
function createOneRosterNamespace(client) {
|
|
56031
56714
|
return {
|
|
56032
56715
|
classes: {
|
|
56033
|
-
create: async (data) => client["request"](
|
|
56716
|
+
create: async (data) => client["request"](ONEROSTER_ENDPOINTS2.classes, "POST", { class: data }),
|
|
56034
56717
|
get: async (sourcedId) => {
|
|
56035
|
-
const res = await client["request"](`${
|
|
56718
|
+
const res = await client["request"](`${ONEROSTER_ENDPOINTS2.classes}/${sourcedId}`, "GET");
|
|
56036
56719
|
return res.class;
|
|
56037
56720
|
},
|
|
56038
56721
|
update: async (sourcedId, data) => {
|
|
56039
|
-
const res = await client["request"](`${
|
|
56722
|
+
const res = await client["request"](`${ONEROSTER_ENDPOINTS2.classes}/${sourcedId}`, "PUT", { class: data });
|
|
56040
56723
|
return res.class;
|
|
56041
56724
|
},
|
|
56042
56725
|
listByCourse: async (courseSourcedId) => {
|
|
56043
|
-
const res = await client["request"](`${
|
|
56726
|
+
const res = await client["request"](`${ONEROSTER_ENDPOINTS2.courses}/${courseSourcedId}/classes`, "GET");
|
|
56044
56727
|
return res.classes;
|
|
56045
56728
|
},
|
|
56046
56729
|
listByStudent: async (userSourcedId, options) => {
|
|
@@ -56051,7 +56734,7 @@ function createOneRosterNamespace(client) {
|
|
|
56051
56734
|
if (options?.offset) {
|
|
56052
56735
|
queryParams.set("offset", String(options.offset));
|
|
56053
56736
|
}
|
|
56054
|
-
const endpoint = `${
|
|
56737
|
+
const endpoint = `${ONEROSTER_ENDPOINTS2.users}/${userSourcedId}/classes`;
|
|
56055
56738
|
const url2 = queryParams.toString() ? `${endpoint}?${queryParams}` : endpoint;
|
|
56056
56739
|
const res = await client["request"](url2, "GET");
|
|
56057
56740
|
return res.classes || [];
|
|
@@ -56059,7 +56742,7 @@ function createOneRosterNamespace(client) {
|
|
|
56059
56742
|
},
|
|
56060
56743
|
enrollments: {
|
|
56061
56744
|
get: async (sourcedId) => {
|
|
56062
|
-
const response = await client["request"](`${
|
|
56745
|
+
const response = await client["request"](`${ONEROSTER_ENDPOINTS2.enrollments}/${sourcedId}`, "GET");
|
|
56063
56746
|
return response.enrollment;
|
|
56064
56747
|
},
|
|
56065
56748
|
listByClass: async (classSourcedId, options) => {
|
|
@@ -56075,7 +56758,7 @@ function createOneRosterNamespace(client) {
|
|
|
56075
56758
|
if (options?.offset) {
|
|
56076
56759
|
queryParams.set("offset", String(options.offset));
|
|
56077
56760
|
}
|
|
56078
|
-
const url2 = `${
|
|
56761
|
+
const url2 = `${ONEROSTER_ENDPOINTS2.enrollments}?${queryParams}`;
|
|
56079
56762
|
try {
|
|
56080
56763
|
const response = await client["request"](url2, "GET");
|
|
56081
56764
|
return response.enrollments || [];
|
|
@@ -56108,7 +56791,7 @@ function createOneRosterNamespace(client) {
|
|
|
56108
56791
|
}
|
|
56109
56792
|
queryParams.set("filter", filters.join(" AND "));
|
|
56110
56793
|
queryParams.set("limit", "3000");
|
|
56111
|
-
const url2 = `${
|
|
56794
|
+
const url2 = `${ONEROSTER_ENDPOINTS2.enrollments}?${queryParams}`;
|
|
56112
56795
|
const response = await client["request"](url2, "GET");
|
|
56113
56796
|
return response.enrollments || [];
|
|
56114
56797
|
}));
|
|
@@ -56138,72 +56821,72 @@ function createOneRosterNamespace(client) {
|
|
|
56138
56821
|
throw error;
|
|
56139
56822
|
}
|
|
56140
56823
|
},
|
|
56141
|
-
create: async (data) => client["request"](
|
|
56824
|
+
create: async (data) => client["request"](ONEROSTER_ENDPOINTS2.enrollments, "POST", { enrollment: data }),
|
|
56142
56825
|
update: async (sourcedId, data) => {
|
|
56143
|
-
await client["request"](`${
|
|
56826
|
+
await client["request"](`${ONEROSTER_ENDPOINTS2.enrollments}/${sourcedId}`, "PUT", {
|
|
56144
56827
|
enrollment: data
|
|
56145
56828
|
});
|
|
56146
56829
|
},
|
|
56147
56830
|
delete: async (sourcedId) => {
|
|
56148
|
-
await client["request"](`${
|
|
56831
|
+
await client["request"](`${ONEROSTER_ENDPOINTS2.enrollments}/${sourcedId}`, "DELETE");
|
|
56149
56832
|
}
|
|
56150
56833
|
},
|
|
56151
56834
|
organizations: {
|
|
56152
|
-
create: async (data) => client["request"](
|
|
56153
|
-
get: async (sourcedId) => client["request"](`${
|
|
56154
|
-
update: async (sourcedId, data) => client["request"](`${
|
|
56835
|
+
create: async (data) => client["request"](ONEROSTER_ENDPOINTS2.organizations, "POST", data),
|
|
56836
|
+
get: async (sourcedId) => client["request"](`${ONEROSTER_ENDPOINTS2.organizations}/${sourcedId}`, "GET").then((res) => res.org),
|
|
56837
|
+
update: async (sourcedId, data) => client["request"](`${ONEROSTER_ENDPOINTS2.organizations}/${sourcedId}`, "PUT", data)
|
|
56155
56838
|
},
|
|
56156
56839
|
courses: {
|
|
56157
|
-
create: async (data) => client["request"](
|
|
56840
|
+
create: async (data) => client["request"](ONEROSTER_ENDPOINTS2.courses, "POST", data),
|
|
56158
56841
|
get: async (sourcedId) => {
|
|
56159
|
-
const response = await client["request"](`${
|
|
56842
|
+
const response = await client["request"](`${ONEROSTER_ENDPOINTS2.courses}/${sourcedId}`, "GET");
|
|
56160
56843
|
return response.course;
|
|
56161
56844
|
},
|
|
56162
56845
|
update: async (sourcedId, data) => {
|
|
56163
|
-
await client["request"](`${
|
|
56846
|
+
await client["request"](`${ONEROSTER_ENDPOINTS2.courses}/${sourcedId}`, "PUT", {
|
|
56164
56847
|
course: data
|
|
56165
56848
|
});
|
|
56166
56849
|
}
|
|
56167
56850
|
},
|
|
56168
56851
|
courseComponents: {
|
|
56169
|
-
create: async (data) => client["request"](
|
|
56852
|
+
create: async (data) => client["request"](ONEROSTER_ENDPOINTS2.courseComponents, "POST", data),
|
|
56170
56853
|
get: async (sourcedId) => {
|
|
56171
|
-
const response = await client["request"](`${
|
|
56854
|
+
const response = await client["request"](`${ONEROSTER_ENDPOINTS2.courseComponents}/${sourcedId}`, "GET");
|
|
56172
56855
|
return response.courseComponent;
|
|
56173
56856
|
},
|
|
56174
56857
|
update: async (sourcedId, data) => {
|
|
56175
|
-
await client["request"](`${
|
|
56858
|
+
await client["request"](`${ONEROSTER_ENDPOINTS2.courseComponents}/${sourcedId}`, "PUT", { courseComponent: data });
|
|
56176
56859
|
}
|
|
56177
56860
|
},
|
|
56178
56861
|
resources: {
|
|
56179
|
-
create: async (data) => client["request"](
|
|
56862
|
+
create: async (data) => client["request"](ONEROSTER_ENDPOINTS2.resources, "POST", data),
|
|
56180
56863
|
get: async (sourcedId) => {
|
|
56181
|
-
const response = await client["request"](`${
|
|
56864
|
+
const response = await client["request"](`${ONEROSTER_ENDPOINTS2.resources}/${sourcedId}`, "GET");
|
|
56182
56865
|
return response.resource;
|
|
56183
56866
|
},
|
|
56184
56867
|
update: async (sourcedId, data) => {
|
|
56185
|
-
await client["request"](`${
|
|
56868
|
+
await client["request"](`${ONEROSTER_ENDPOINTS2.resources}/${sourcedId}`, "PUT", {
|
|
56186
56869
|
resource: data
|
|
56187
56870
|
});
|
|
56188
56871
|
},
|
|
56189
56872
|
delete: async (sourcedId) => {
|
|
56190
|
-
await client["request"](`${
|
|
56873
|
+
await client["request"](`${ONEROSTER_ENDPOINTS2.resources}/${sourcedId}`, "DELETE");
|
|
56191
56874
|
}
|
|
56192
56875
|
},
|
|
56193
56876
|
componentResources: {
|
|
56194
|
-
create: async (data) => client["request"](
|
|
56877
|
+
create: async (data) => client["request"](ONEROSTER_ENDPOINTS2.componentResources, "POST", data),
|
|
56195
56878
|
get: async (sourcedId) => {
|
|
56196
|
-
const response = await client["request"](`${
|
|
56879
|
+
const response = await client["request"](`${ONEROSTER_ENDPOINTS2.componentResources}/${sourcedId}`, "GET");
|
|
56197
56880
|
return response.componentResource;
|
|
56198
56881
|
},
|
|
56199
56882
|
update: async (sourcedId, data) => {
|
|
56200
|
-
await client["request"](`${
|
|
56883
|
+
await client["request"](`${ONEROSTER_ENDPOINTS2.componentResources}/${sourcedId}`, "PUT", { componentResource: data });
|
|
56201
56884
|
}
|
|
56202
56885
|
},
|
|
56203
56886
|
assessmentLineItems: {
|
|
56204
56887
|
list: async (options) => {
|
|
56205
56888
|
try {
|
|
56206
|
-
return await listOneRosterCollection(client,
|
|
56889
|
+
return await listOneRosterCollection(client, ONEROSTER_ENDPOINTS2.assessmentLineItems, "assessmentLineItems", options);
|
|
56207
56890
|
} catch (error) {
|
|
56208
56891
|
logTimebackError("list assessment line items", error, {
|
|
56209
56892
|
filter: options?.filter
|
|
@@ -56211,14 +56894,14 @@ function createOneRosterNamespace(client) {
|
|
|
56211
56894
|
return [];
|
|
56212
56895
|
}
|
|
56213
56896
|
},
|
|
56214
|
-
create: async (data) => client["request"](
|
|
56897
|
+
create: async (data) => client["request"](ONEROSTER_ENDPOINTS2.assessmentLineItems, "POST", { assessmentLineItem: data }),
|
|
56215
56898
|
get: async (sourcedId) => {
|
|
56216
|
-
const response = await client["request"](`${
|
|
56899
|
+
const response = await client["request"](`${ONEROSTER_ENDPOINTS2.assessmentLineItems}/${sourcedId}`, "GET");
|
|
56217
56900
|
return response.assessmentLineItem;
|
|
56218
56901
|
},
|
|
56219
56902
|
findOrCreate: async (sourcedId, data) => {
|
|
56220
56903
|
try {
|
|
56221
|
-
const response = await client["request"](`${
|
|
56904
|
+
const response = await client["request"](`${ONEROSTER_ENDPOINTS2.assessmentLineItems}/${sourcedId}`, "GET");
|
|
56222
56905
|
return response.assessmentLineItem;
|
|
56223
56906
|
} catch {
|
|
56224
56907
|
const createData = {
|
|
@@ -56227,7 +56910,7 @@ function createOneRosterNamespace(client) {
|
|
|
56227
56910
|
dateLastModified: new Date().toISOString()
|
|
56228
56911
|
};
|
|
56229
56912
|
try {
|
|
56230
|
-
const response = await client["request"](
|
|
56913
|
+
const response = await client["request"](ONEROSTER_ENDPOINTS2.assessmentLineItems, "POST", { assessmentLineItem: createData });
|
|
56231
56914
|
if (!response.sourcedIdPairs?.allocatedSourcedId) {
|
|
56232
56915
|
throw new Error("Invalid response from OneRoster API - missing allocatedSourcedId");
|
|
56233
56916
|
}
|
|
@@ -56242,7 +56925,7 @@ function createOneRosterNamespace(client) {
|
|
|
56242
56925
|
assessmentResults: {
|
|
56243
56926
|
list: async (options) => {
|
|
56244
56927
|
try {
|
|
56245
|
-
return await listOneRosterCollection(client,
|
|
56928
|
+
return await listOneRosterCollection(client, ONEROSTER_ENDPOINTS2.assessmentResults, "assessmentResults", options);
|
|
56246
56929
|
} catch (error) {
|
|
56247
56930
|
logTimebackError("list assessment results", error, {
|
|
56248
56931
|
filter: options?.filter
|
|
@@ -56250,10 +56933,11 @@ function createOneRosterNamespace(client) {
|
|
|
56250
56933
|
return [];
|
|
56251
56934
|
}
|
|
56252
56935
|
},
|
|
56253
|
-
|
|
56936
|
+
listOrThrow: async (options) => await listOneRosterCollection(client, ONEROSTER_ENDPOINTS2.assessmentResults, "assessmentResults", options),
|
|
56937
|
+
create: async (data) => client["request"](ONEROSTER_ENDPOINTS2.assessmentResults, "POST", { assessmentResult: data }),
|
|
56254
56938
|
listByStudent: async (studentSourcedId, options) => {
|
|
56255
56939
|
try {
|
|
56256
|
-
return await listOneRosterCollection(client,
|
|
56940
|
+
return await listOneRosterCollection(client, ONEROSTER_ENDPOINTS2.assessmentResults, "assessmentResults", {
|
|
56257
56941
|
limit: options?.limit,
|
|
56258
56942
|
offset: options?.offset,
|
|
56259
56943
|
fields: options?.fields,
|
|
@@ -56272,54 +56956,22 @@ function createOneRosterNamespace(client) {
|
|
|
56272
56956
|
sourcedId,
|
|
56273
56957
|
dateLastModified: new Date().toISOString()
|
|
56274
56958
|
};
|
|
56275
|
-
return client["request"](`${
|
|
56959
|
+
return client["request"](`${ONEROSTER_ENDPOINTS2.assessmentResults}/${sourcedId}`, "PUT", { assessmentResult });
|
|
56276
56960
|
},
|
|
56277
56961
|
get: async (sourcedId) => {
|
|
56278
|
-
const response = await client["request"](`${
|
|
56962
|
+
const response = await client["request"](`${ONEROSTER_ENDPOINTS2.assessmentResults}/${sourcedId}`, "GET");
|
|
56279
56963
|
return response.assessmentResult;
|
|
56280
56964
|
},
|
|
56281
|
-
update: async (sourcedId, data) => client["request"](`${
|
|
56282
|
-
getAttemptStats: async (studentId, lineItemId) => {
|
|
56283
|
-
try {
|
|
56284
|
-
const filter = `student.sourcedId='${studentId}' AND assessmentLineItem.sourcedId='${lineItemId}'`;
|
|
56285
|
-
const url2 = `${ONEROSTER_ENDPOINTS.assessmentResults}?filter=${encodeURIComponent(filter)}`;
|
|
56286
|
-
const response = await client["request"](url2, "GET");
|
|
56287
|
-
const results = response.assessmentResults || [];
|
|
56288
|
-
const firstResult = results[0];
|
|
56289
|
-
if (!firstResult) {
|
|
56290
|
-
return null;
|
|
56291
|
-
}
|
|
56292
|
-
let maxAttemptResult = firstResult;
|
|
56293
|
-
let maxAttemptNumber = maxAttemptResult.metadata?.attemptNumber || 0;
|
|
56294
|
-
let activeAttemptCount = 0;
|
|
56295
|
-
for (const result of results) {
|
|
56296
|
-
const attemptNumber = result.metadata?.attemptNumber || 0;
|
|
56297
|
-
if (attemptNumber > maxAttemptNumber) {
|
|
56298
|
-
maxAttemptNumber = attemptNumber;
|
|
56299
|
-
maxAttemptResult = result;
|
|
56300
|
-
}
|
|
56301
|
-
if (result.status === "active") {
|
|
56302
|
-
activeAttemptCount++;
|
|
56303
|
-
}
|
|
56304
|
-
}
|
|
56305
|
-
return { maxAttemptNumber, activeAttemptCount, maxAttemptResult };
|
|
56306
|
-
} catch (error) {
|
|
56307
|
-
logTimebackError("query attempt stats", error, {
|
|
56308
|
-
studentId,
|
|
56309
|
-
lineItemId
|
|
56310
|
-
});
|
|
56311
|
-
return null;
|
|
56312
|
-
}
|
|
56313
|
-
}
|
|
56965
|
+
update: async (sourcedId, data) => client["request"](`${ONEROSTER_ENDPOINTS2.assessmentResults}/${sourcedId}`, "PUT", data)
|
|
56314
56966
|
},
|
|
56315
56967
|
users: {
|
|
56316
|
-
create: async (data) => client["request"](
|
|
56968
|
+
create: async (data) => client["request"](ONEROSTER_ENDPOINTS2.users, "POST", {
|
|
56317
56969
|
user: data
|
|
56318
56970
|
}),
|
|
56319
|
-
get: async (sourcedId) => client["request"](`${
|
|
56971
|
+
get: async (sourcedId) => client["request"](`${ONEROSTER_ENDPOINTS2.users}/${sourcedId}`, "GET").then((res) => res.user),
|
|
56320
56972
|
findByEmail: async (email) => {
|
|
56321
56973
|
const params = new URLSearchParams({ filter: `email='${email}'` });
|
|
56322
|
-
const response = await client["request"](`${
|
|
56974
|
+
const response = await client["request"](`${ONEROSTER_ENDPOINTS2.users}?${params}`, "GET");
|
|
56323
56975
|
if (!response || !response.users || !Array.isArray(response.users)) {
|
|
56324
56976
|
throw new Error(`Invalid response format from OneRoster API when searching for user with email: ${email}. Expected { users: [...] } but received: ${JSON.stringify(response)}`);
|
|
56325
56977
|
}
|
|
@@ -56338,7 +56990,7 @@ function createOneRosterNamespace(client) {
|
|
|
56338
56990
|
const results = await Promise.all(batches.map(async (batch) => {
|
|
56339
56991
|
const filter = batch.map((id) => `sourcedId='${escapeFilterValue(id)}'`).join(" OR ");
|
|
56340
56992
|
const params = new URLSearchParams({ filter });
|
|
56341
|
-
const response = await client["request"](`${
|
|
56993
|
+
const response = await client["request"](`${ONEROSTER_ENDPOINTS2.users}?${params}`, "GET");
|
|
56342
56994
|
return response.users || [];
|
|
56343
56995
|
}));
|
|
56344
56996
|
return results.flat();
|
|
@@ -56355,11 +57007,11 @@ function createOneRosterNamespace(client) {
|
|
|
56355
57007
|
function createQtiNamespace(client) {
|
|
56356
57008
|
return {
|
|
56357
57009
|
items: {
|
|
56358
|
-
create: async (data) => client["requestQti"](
|
|
56359
|
-
get: async (identifier) => client["requestQti"](`${
|
|
56360
|
-
update: async (identifier, data) => client["requestQti"](`${
|
|
57010
|
+
create: async (data) => client["requestQti"](QTI_ENDPOINTS2.assessmentItems, "POST", data),
|
|
57011
|
+
get: async (identifier) => client["requestQti"](`${QTI_ENDPOINTS2.assessmentItems}/${identifier}`, "GET"),
|
|
57012
|
+
update: async (identifier, data) => client["requestQti"](`${QTI_ENDPOINTS2.assessmentItems}/${identifier}`, "PUT", data),
|
|
56361
57013
|
delete: async (identifier) => {
|
|
56362
|
-
await client["requestQti"](`${
|
|
57014
|
+
await client["requestQti"](`${QTI_ENDPOINTS2.assessmentItems}/${identifier}`, "DELETE");
|
|
56363
57015
|
}
|
|
56364
57016
|
},
|
|
56365
57017
|
tests: {
|
|
@@ -56381,25 +57033,25 @@ function createQtiNamespace(client) {
|
|
|
56381
57033
|
params.set("order", options.order);
|
|
56382
57034
|
}
|
|
56383
57035
|
const query = params.toString();
|
|
56384
|
-
return client["requestQti"](`${
|
|
57036
|
+
return client["requestQti"](`${QTI_ENDPOINTS2.assessmentTests}${query ? `?${query}` : ""}`, "GET");
|
|
56385
57037
|
},
|
|
56386
|
-
create: async (data) => client["requestQti"](
|
|
56387
|
-
get: async (identifier) => client["requestQti"](`${
|
|
56388
|
-
getQuestions: async (identifier) => client["requestQti"](`${
|
|
56389
|
-
update: async (identifier, data) => client["requestQti"](`${
|
|
57038
|
+
create: async (data) => client["requestQti"](QTI_ENDPOINTS2.assessmentTests, "POST", data),
|
|
57039
|
+
get: async (identifier) => client["requestQti"](`${QTI_ENDPOINTS2.assessmentTests}/${identifier}`, "GET"),
|
|
57040
|
+
getQuestions: async (identifier) => client["requestQti"](`${QTI_ENDPOINTS2.assessmentTests}/${identifier}/questions`, "GET"),
|
|
57041
|
+
update: async (identifier, data) => client["requestQti"](`${QTI_ENDPOINTS2.assessmentTests}/${identifier}`, "PUT", data),
|
|
56390
57042
|
delete: async (identifier) => {
|
|
56391
|
-
await client["requestQti"](`${
|
|
57043
|
+
await client["requestQti"](`${QTI_ENDPOINTS2.assessmentTests}/${identifier}`, "DELETE");
|
|
56392
57044
|
},
|
|
56393
|
-
addItem: async (testId, partId, sectionId, itemIdentifier) => client["requestQti"](`${
|
|
57045
|
+
addItem: async (testId, partId, sectionId, itemIdentifier) => client["requestQti"](`${QTI_ENDPOINTS2.assessmentTests}/${testId}/test-parts/${partId}/sections/${sectionId}/items`, "POST", { identifier: itemIdentifier }),
|
|
56394
57046
|
removeItem: async (testId, partId, sectionId, itemIdentifier) => {
|
|
56395
|
-
await client["requestQti"](`${
|
|
57047
|
+
await client["requestQti"](`${QTI_ENDPOINTS2.assessmentTests}/${testId}/test-parts/${partId}/sections/${sectionId}/items/${itemIdentifier}`, "DELETE");
|
|
56396
57048
|
},
|
|
56397
|
-
reorderItems: async (testId, partId, sectionId, items) => client["requestQti"](`${
|
|
57049
|
+
reorderItems: async (testId, partId, sectionId, items) => client["requestQti"](`${QTI_ENDPOINTS2.assessmentTests}/${testId}/test-parts/${partId}/sections/${sectionId}/items/order`, "PUT", { items })
|
|
56398
57050
|
}
|
|
56399
57051
|
};
|
|
56400
57052
|
}
|
|
56401
57053
|
function toCaliperSubject(subject) {
|
|
56402
|
-
return
|
|
57054
|
+
return isTimebackSubject3(subject) ? subject : "None";
|
|
56403
57055
|
}
|
|
56404
57056
|
function buildAdminEventMetadata({
|
|
56405
57057
|
reason,
|
|
@@ -56463,7 +57115,7 @@ class AdminEventRecorder {
|
|
|
56463
57115
|
defaultActivityId: "playcademy-admin-manual-xp",
|
|
56464
57116
|
eventKind: "remediation-xp"
|
|
56465
57117
|
});
|
|
56466
|
-
const courseUrl = createOneRosterUrls(
|
|
57118
|
+
const courseUrl = createOneRosterUrls(TIMEBACK_API_URLS2[this.environment]).course(data.courseId);
|
|
56467
57119
|
await this.caliper.emitActivityEvent({
|
|
56468
57120
|
studentId: ctx.student.id,
|
|
56469
57121
|
studentEmail: ctx.student.email,
|
|
@@ -56513,7 +57165,7 @@ class AdminEventRecorder {
|
|
|
56513
57165
|
defaultActivityId: "playcademy-admin-mastery-adjustment",
|
|
56514
57166
|
eventKind: "remediation-mastery"
|
|
56515
57167
|
});
|
|
56516
|
-
const courseUrl = createOneRosterUrls(
|
|
57168
|
+
const courseUrl = createOneRosterUrls(TIMEBACK_API_URLS2[this.environment]).course(data.courseId);
|
|
56517
57169
|
await this.caliper.emitActivityEvent({
|
|
56518
57170
|
studentId: ctx.student.id,
|
|
56519
57171
|
studentEmail: ctx.student.email,
|
|
@@ -56547,9 +57199,9 @@ class TimebackCache {
|
|
|
56547
57199
|
maxSize;
|
|
56548
57200
|
name;
|
|
56549
57201
|
constructor(options = {}) {
|
|
56550
|
-
this.defaultTTL = options.defaultTTL ||
|
|
56551
|
-
this.maxSize = options.maxSize ||
|
|
56552
|
-
this.name = options.name ||
|
|
57202
|
+
this.defaultTTL = options.defaultTTL || CACHE_DEFAULTS2.defaultTTL;
|
|
57203
|
+
this.maxSize = options.maxSize || CACHE_DEFAULTS2.defaultMaxSize;
|
|
57204
|
+
this.name = options.name || CACHE_DEFAULTS2.defaultName;
|
|
56553
57205
|
}
|
|
56554
57206
|
get(key) {
|
|
56555
57207
|
const entry = this.cache.get(key);
|
|
@@ -56640,23 +57292,23 @@ class TimebackCacheManager {
|
|
|
56640
57292
|
enrollmentCache;
|
|
56641
57293
|
constructor() {
|
|
56642
57294
|
this.studentCache = new TimebackCache({
|
|
56643
|
-
defaultTTL:
|
|
56644
|
-
maxSize:
|
|
57295
|
+
defaultTTL: CACHE_DEFAULTS2.studentTTL,
|
|
57296
|
+
maxSize: CACHE_DEFAULTS2.studentMaxSize,
|
|
56645
57297
|
name: "StudentCache"
|
|
56646
57298
|
});
|
|
56647
57299
|
this.assessmentLineItemCache = new TimebackCache({
|
|
56648
|
-
defaultTTL:
|
|
56649
|
-
maxSize:
|
|
57300
|
+
defaultTTL: CACHE_DEFAULTS2.assessmentTTL,
|
|
57301
|
+
maxSize: CACHE_DEFAULTS2.assessmentMaxSize,
|
|
56650
57302
|
name: "AssessmentLineItemCache"
|
|
56651
57303
|
});
|
|
56652
57304
|
this.resourceMasteryCache = new TimebackCache({
|
|
56653
|
-
defaultTTL:
|
|
56654
|
-
maxSize:
|
|
57305
|
+
defaultTTL: CACHE_DEFAULTS2.assessmentTTL,
|
|
57306
|
+
maxSize: CACHE_DEFAULTS2.assessmentMaxSize,
|
|
56655
57307
|
name: "ResourceMasteryCache"
|
|
56656
57308
|
});
|
|
56657
57309
|
this.enrollmentCache = new TimebackCache({
|
|
56658
|
-
defaultTTL:
|
|
56659
|
-
maxSize:
|
|
57310
|
+
defaultTTL: CACHE_DEFAULTS2.enrollmentTTL,
|
|
57311
|
+
maxSize: CACHE_DEFAULTS2.enrollmentMaxSize,
|
|
56660
57312
|
name: "EnrollmentCache"
|
|
56661
57313
|
});
|
|
56662
57314
|
}
|
|
@@ -56912,18 +57564,18 @@ class MasteryTracker {
|
|
|
56912
57564
|
await this.onerosterNamespace.assessmentLineItems.findOrCreate(lineItemId, {
|
|
56913
57565
|
sourcedId: lineItemId,
|
|
56914
57566
|
title: "Mastery Completion",
|
|
56915
|
-
status:
|
|
57567
|
+
status: ONEROSTER_STATUS2.active,
|
|
56916
57568
|
...classId ? { class: { sourcedId: classId } } : { course: { sourcedId: ids.course } },
|
|
56917
57569
|
...ids.componentResource ? { componentResource: { sourcedId: ids.componentResource } } : {}
|
|
56918
57570
|
});
|
|
56919
57571
|
await this.onerosterNamespace.assessmentResults.upsert(resultId, {
|
|
56920
57572
|
sourcedId: resultId,
|
|
56921
|
-
status:
|
|
57573
|
+
status: ONEROSTER_STATUS2.active,
|
|
56922
57574
|
assessmentLineItem: { sourcedId: lineItemId },
|
|
56923
57575
|
student: { sourcedId: studentId },
|
|
56924
57576
|
score: 100,
|
|
56925
57577
|
scoreDate: new Date().toISOString(),
|
|
56926
|
-
scoreStatus:
|
|
57578
|
+
scoreStatus: SCORE_STATUS2.fullyGraded,
|
|
56927
57579
|
inProgress: "false",
|
|
56928
57580
|
metadata: {
|
|
56929
57581
|
isMasteryCompletion: true,
|
|
@@ -56935,6 +57587,7 @@ class MasteryTracker {
|
|
|
56935
57587
|
"app.timeback.line_item_id": lineItemId,
|
|
56936
57588
|
"app.timeback.result_id": resultId
|
|
56937
57589
|
});
|
|
57590
|
+
return true;
|
|
56938
57591
|
} catch (error) {
|
|
56939
57592
|
addEvent("timeback.mastery_completion_failed", {
|
|
56940
57593
|
"app.timeback.student_id": studentId,
|
|
@@ -56942,6 +57595,7 @@ class MasteryTracker {
|
|
|
56942
57595
|
"exception.type": errorType(error),
|
|
56943
57596
|
"app.error.message": errorMessage(error)
|
|
56944
57597
|
});
|
|
57598
|
+
return false;
|
|
56945
57599
|
}
|
|
56946
57600
|
}
|
|
56947
57601
|
async revokeCompletionEntry(studentId, courseId, classId, appName) {
|
|
@@ -56952,18 +57606,18 @@ class MasteryTracker {
|
|
|
56952
57606
|
await this.onerosterNamespace.assessmentLineItems.findOrCreate(lineItemId, {
|
|
56953
57607
|
sourcedId: lineItemId,
|
|
56954
57608
|
title: "Mastery Completion",
|
|
56955
|
-
status:
|
|
57609
|
+
status: ONEROSTER_STATUS2.active,
|
|
56956
57610
|
...classId ? { class: { sourcedId: classId } } : { course: { sourcedId: ids.course } },
|
|
56957
57611
|
...ids.componentResource ? { componentResource: { sourcedId: ids.componentResource } } : {}
|
|
56958
57612
|
});
|
|
56959
57613
|
await this.onerosterNamespace.assessmentResults.upsert(resultId, {
|
|
56960
57614
|
sourcedId: resultId,
|
|
56961
|
-
status:
|
|
57615
|
+
status: ONEROSTER_STATUS2.active,
|
|
56962
57616
|
assessmentLineItem: { sourcedId: lineItemId },
|
|
56963
57617
|
student: { sourcedId: studentId },
|
|
56964
57618
|
score: 0,
|
|
56965
57619
|
scoreDate: new Date().toISOString(),
|
|
56966
|
-
scoreStatus:
|
|
57620
|
+
scoreStatus: SCORE_STATUS2.notSubmitted,
|
|
56967
57621
|
inProgress: "true",
|
|
56968
57622
|
metadata: {
|
|
56969
57623
|
isMasteryCompletion: true,
|
|
@@ -56998,7 +57652,7 @@ class MasteryTracker {
|
|
|
56998
57652
|
if (!playcademyMetadata) {
|
|
56999
57653
|
return;
|
|
57000
57654
|
}
|
|
57001
|
-
const masterableUnits =
|
|
57655
|
+
const masterableUnits = isPlaycademyResourceMetadata2(playcademyMetadata) ? playcademyMetadata.mastery?.masterableUnits : undefined;
|
|
57002
57656
|
this.cacheManager.setResourceMasterableUnits(resourceId, masterableUnits ?? null);
|
|
57003
57657
|
return masterableUnits;
|
|
57004
57658
|
} catch (error) {
|
|
@@ -57078,13 +57732,11 @@ function validateSessionData(sessionData) {
|
|
|
57078
57732
|
|
|
57079
57733
|
class ProgressRecorder {
|
|
57080
57734
|
studentResolver;
|
|
57081
|
-
cacheManager;
|
|
57082
57735
|
onerosterNamespace;
|
|
57083
57736
|
caliperNamespace;
|
|
57084
57737
|
masteryTracker;
|
|
57085
|
-
constructor(studentResolver,
|
|
57738
|
+
constructor(studentResolver, onerosterNamespace, caliperNamespace, masteryTracker) {
|
|
57086
57739
|
this.studentResolver = studentResolver;
|
|
57087
|
-
this.cacheManager = cacheManager;
|
|
57088
57740
|
this.onerosterNamespace = onerosterNamespace;
|
|
57089
57741
|
this.caliperNamespace = caliperNamespace;
|
|
57090
57742
|
this.masteryTracker = masteryTracker;
|
|
@@ -57093,26 +57745,37 @@ class ProgressRecorder {
|
|
|
57093
57745
|
validateProgressData(progressData);
|
|
57094
57746
|
const { ids, activityId, activityName, courseName, student } = await this.resolveContext(courseId, studentIdentifier, progressData);
|
|
57095
57747
|
const { id: studentId, email: studentEmail } = student;
|
|
57096
|
-
const {
|
|
57097
|
-
|
|
57098
|
-
|
|
57099
|
-
|
|
57100
|
-
|
|
57101
|
-
attemptNumber
|
|
57102
|
-
} = progressData;
|
|
57103
|
-
let extensions = progressData.extensions;
|
|
57104
|
-
const masteryProgress = await this.masteryTracker.checkProgress({
|
|
57105
|
-
studentId,
|
|
57106
|
-
courseId,
|
|
57107
|
-
resourceId: ids.resource,
|
|
57108
|
-
masteredUnits: progressData.masteredUnits ?? 0,
|
|
57109
|
-
masteredUnitsAbsolute: progressData.masteredUnitsAbsolute
|
|
57748
|
+
const { totalQuestions, correctQuestions, xpEarned = 0, attemptNumber } = progressData;
|
|
57749
|
+
const activityUrl = this.caliperNamespace.buildActivityUrl({
|
|
57750
|
+
courseId: ids.course,
|
|
57751
|
+
activityId,
|
|
57752
|
+
sensorUrl: progressData.sensorUrl
|
|
57110
57753
|
});
|
|
57754
|
+
const courseUrl = this.caliperNamespace.buildCourseUrl(ids.course);
|
|
57755
|
+
let caliperLineItemId;
|
|
57756
|
+
try {
|
|
57757
|
+
caliperLineItemId = computeCaliperLineItemId(activityUrl, ids.course, courseUrl);
|
|
57758
|
+
} catch (error) {
|
|
57759
|
+
setAttributes({ "app.timeback.course_id_not_url_safe": true });
|
|
57760
|
+
throw error;
|
|
57761
|
+
}
|
|
57762
|
+
const legacyLineItemId = `${ids.course}-${activityId}-assessment`;
|
|
57763
|
+
const [currentAttemptNumber, masteryProgress] = await Promise.all([
|
|
57764
|
+
this.resolveAttemptNumber(attemptNumber, studentId, caliperLineItemId, legacyLineItemId),
|
|
57765
|
+
this.masteryTracker.checkProgress({
|
|
57766
|
+
studentId,
|
|
57767
|
+
courseId,
|
|
57768
|
+
resourceId: ids.resource,
|
|
57769
|
+
masteredUnits: progressData.masteredUnits ?? 0,
|
|
57770
|
+
masteredUnitsAbsolute: progressData.masteredUnitsAbsolute
|
|
57771
|
+
})
|
|
57772
|
+
]);
|
|
57773
|
+
setAttributes({ "app.timeback.attempt_number": currentAttemptNumber });
|
|
57774
|
+
let extensions = progressData.extensions;
|
|
57111
57775
|
const effectiveMasteredUnits = masteryProgress ? masteryProgress.effectiveDelta : progressData.masteredUnits ?? 0;
|
|
57112
57776
|
let pctCompleteApp;
|
|
57113
57777
|
let masteryAchieved = false;
|
|
57114
|
-
let
|
|
57115
|
-
const inProgress = "false";
|
|
57778
|
+
let completionEntryWritten = false;
|
|
57116
57779
|
const warnings = masteryProgress?.writeWarning ? [masteryProgress.writeWarning] : undefined;
|
|
57117
57780
|
if (masteryProgress) {
|
|
57118
57781
|
masteryAchieved = masteryProgress.masteryAchieved;
|
|
@@ -57121,32 +57784,12 @@ class ProgressRecorder {
|
|
|
57121
57784
|
...extensions,
|
|
57122
57785
|
...pctCompleteApp !== undefined ? { pctCompleteApp } : {}
|
|
57123
57786
|
};
|
|
57124
|
-
if (masteryAchieved) {
|
|
57125
|
-
scoreStatus = SCORE_STATUS.fullyGraded;
|
|
57126
|
-
}
|
|
57127
57787
|
}
|
|
57128
|
-
|
|
57129
|
-
|
|
57130
|
-
if (score !== undefined) {
|
|
57131
|
-
await this.createGradebookEntry({
|
|
57132
|
-
lineItemId: actualLineItemId,
|
|
57133
|
-
studentId,
|
|
57134
|
-
attemptNumber: currentAttemptNumber,
|
|
57135
|
-
score,
|
|
57136
|
-
xp: xpEarned,
|
|
57137
|
-
scoreStatus,
|
|
57138
|
-
inProgress,
|
|
57139
|
-
appName: progressData.appName,
|
|
57140
|
-
totalQuestions,
|
|
57141
|
-
correctQuestions,
|
|
57142
|
-
masteredUnits: effectiveMasteredUnits || undefined,
|
|
57143
|
-
pctCompleteApp
|
|
57144
|
-
});
|
|
57145
|
-
} else {
|
|
57146
|
-
setAttribute("app.timeback.score_provided", false);
|
|
57788
|
+
if (masteryAchieved) {
|
|
57789
|
+
setAttributes({ "app.timeback.mastery_achieved": true });
|
|
57147
57790
|
}
|
|
57148
57791
|
if (masteryAchieved) {
|
|
57149
|
-
await this.masteryTracker.createCompletionEntry(studentId, courseId, progressData.classId, progressData.appName);
|
|
57792
|
+
completionEntryWritten = await this.masteryTracker.createCompletionEntry(studentId, courseId, progressData.classId, progressData.appName);
|
|
57150
57793
|
await this.emitCourseCompletionHistoryEvent({
|
|
57151
57794
|
studentId,
|
|
57152
57795
|
studentEmail,
|
|
@@ -57162,30 +57805,36 @@ class ProgressRecorder {
|
|
|
57162
57805
|
if (masteryProgress?.masteryRevoked) {
|
|
57163
57806
|
await this.masteryTracker.revokeCompletionEntry(studentId, courseId, progressData.classId, progressData.appName);
|
|
57164
57807
|
}
|
|
57165
|
-
|
|
57166
|
-
|
|
57167
|
-
|
|
57168
|
-
|
|
57169
|
-
|
|
57170
|
-
|
|
57171
|
-
|
|
57172
|
-
|
|
57173
|
-
|
|
57174
|
-
|
|
57175
|
-
|
|
57176
|
-
|
|
57177
|
-
|
|
57178
|
-
|
|
57179
|
-
|
|
57180
|
-
|
|
57181
|
-
|
|
57808
|
+
try {
|
|
57809
|
+
await this.emitCaliperEvent({
|
|
57810
|
+
studentId,
|
|
57811
|
+
studentEmail,
|
|
57812
|
+
gameId: progressData.gameId,
|
|
57813
|
+
activityId,
|
|
57814
|
+
activityName,
|
|
57815
|
+
courseId: ids.course,
|
|
57816
|
+
courseName,
|
|
57817
|
+
totalQuestions,
|
|
57818
|
+
correctQuestions,
|
|
57819
|
+
xpEarned,
|
|
57820
|
+
masteredUnits: effectiveMasteredUnits || undefined,
|
|
57821
|
+
attemptNumber: currentAttemptNumber,
|
|
57822
|
+
objectId: activityUrl,
|
|
57823
|
+
progressData,
|
|
57824
|
+
extensions,
|
|
57825
|
+
runId: progressData.runId
|
|
57826
|
+
});
|
|
57827
|
+
} catch (error) {
|
|
57828
|
+
if (completionEntryWritten) {
|
|
57829
|
+
setAttributes({ "app.timeback.completion_orphaned": true });
|
|
57830
|
+
}
|
|
57831
|
+
throw error;
|
|
57832
|
+
}
|
|
57182
57833
|
return {
|
|
57183
57834
|
xpAwarded: xpEarned,
|
|
57184
57835
|
attemptNumber: currentAttemptNumber,
|
|
57185
57836
|
masteredUnitsApplied: effectiveMasteredUnits,
|
|
57186
57837
|
pctCompleteApp,
|
|
57187
|
-
scoreStatus,
|
|
57188
|
-
inProgress,
|
|
57189
57838
|
...warnings ? { warnings } : {}
|
|
57190
57839
|
};
|
|
57191
57840
|
}
|
|
@@ -57197,89 +57846,48 @@ class ProgressRecorder {
|
|
|
57197
57846
|
const student = await this.studentResolver.resolve(studentIdentifier, progressData.studentEmail);
|
|
57198
57847
|
return { ids, activityId, activityName, courseName, student };
|
|
57199
57848
|
}
|
|
57200
|
-
async
|
|
57201
|
-
const lineItemId = `${ids.course}-${activityId}-assessment`;
|
|
57202
|
-
let actualLineItemId = this.cacheManager.getAssessmentLineItem(lineItemId);
|
|
57203
|
-
if (!actualLineItemId) {
|
|
57204
|
-
actualLineItemId = await this.getOrCreateLineItem(lineItemId, activityName, classId, ids);
|
|
57205
|
-
this.cacheManager.setAssessmentLineItem(lineItemId, actualLineItemId);
|
|
57206
|
-
}
|
|
57207
|
-
return actualLineItemId;
|
|
57208
|
-
}
|
|
57209
|
-
async resolveAttemptNumber(providedAttemptNumber, score, studentId, lineItemId) {
|
|
57849
|
+
async resolveAttemptNumber(providedAttemptNumber, studentId, caliperLineItemId, legacyLineItemId) {
|
|
57210
57850
|
if (providedAttemptNumber) {
|
|
57851
|
+
setAttributes({ "app.timeback.attempt_source": "provided" });
|
|
57211
57852
|
return providedAttemptNumber;
|
|
57212
57853
|
}
|
|
57213
|
-
|
|
57214
|
-
|
|
57215
|
-
|
|
57216
|
-
|
|
57217
|
-
|
|
57218
|
-
async getOrCreateLineItem(lineItemId, activityName, classId, ids) {
|
|
57219
|
-
try {
|
|
57220
|
-
const lineItem = await this.onerosterNamespace.assessmentLineItems.findOrCreate(lineItemId, {
|
|
57221
|
-
sourcedId: lineItemId,
|
|
57222
|
-
title: activityName,
|
|
57223
|
-
status: ONEROSTER_STATUS.active,
|
|
57224
|
-
...classId ? { class: { sourcedId: classId } } : { course: { sourcedId: ids.course } }
|
|
57854
|
+
const caliperAttempt = await this.getLatestActivityAttempt(studentId, caliperLineItemId, "attempt");
|
|
57855
|
+
if (caliperAttempt !== null) {
|
|
57856
|
+
const next = caliperAttempt + 1;
|
|
57857
|
+
setAttributes({
|
|
57858
|
+
"app.timeback.attempt_source": caliperAttempt > 0 ? "caliper" : "caliper_unreadable"
|
|
57225
57859
|
});
|
|
57226
|
-
if (
|
|
57227
|
-
|
|
57228
|
-
|
|
57229
|
-
|
|
57230
|
-
|
|
57231
|
-
if (error instanceof TimebackApiError && error.status === 404) {
|
|
57232
|
-
const errorDetails = error.details;
|
|
57233
|
-
const description = errorDetails?.imsx_description || error.message;
|
|
57234
|
-
if (description.includes("course") && description.includes("not found") || description.includes("component") && description.includes("not found") || description.includes("resource") && description.includes("not found") || description.includes("componentResource") && description.includes("not found")) {
|
|
57235
|
-
throw new ResourceNotFoundError("TimeBack resources", "setup-not-run");
|
|
57236
|
-
}
|
|
57860
|
+
if (caliperAttempt === 0) {
|
|
57861
|
+
setAttributes({
|
|
57862
|
+
"app.timeback.attempt_regressed": true,
|
|
57863
|
+
"app.timeback.attempt_emitted": next
|
|
57864
|
+
});
|
|
57237
57865
|
}
|
|
57238
|
-
|
|
57866
|
+
return next;
|
|
57239
57867
|
}
|
|
57240
|
-
|
|
57241
|
-
|
|
57242
|
-
|
|
57243
|
-
|
|
57244
|
-
|
|
57868
|
+
const legacyAttempt = await this.getLatestActivityAttempt(studentId, legacyLineItemId, "attemptNumber");
|
|
57869
|
+
if (legacyAttempt !== null) {
|
|
57870
|
+
setAttributes({
|
|
57871
|
+
"app.timeback.attempt_source": "legacy_seed",
|
|
57872
|
+
"app.timeback.legacy_seed_attempt": legacyAttempt
|
|
57873
|
+
});
|
|
57874
|
+
return legacyAttempt + 1;
|
|
57245
57875
|
}
|
|
57876
|
+
setAttributes({ "app.timeback.attempt_source": "first" });
|
|
57246
57877
|
return 1;
|
|
57247
57878
|
}
|
|
57248
|
-
async
|
|
57249
|
-
|
|
57250
|
-
|
|
57251
|
-
|
|
57252
|
-
|
|
57253
|
-
|
|
57254
|
-
scoreStatus,
|
|
57255
|
-
inProgress,
|
|
57256
|
-
appName,
|
|
57257
|
-
totalQuestions,
|
|
57258
|
-
correctQuestions,
|
|
57259
|
-
masteredUnits,
|
|
57260
|
-
pctCompleteApp
|
|
57261
|
-
}) {
|
|
57262
|
-
const timestamp3 = Date.now().toString(36);
|
|
57263
|
-
const resultId = `${lineItemId}:${studentId}:${timestamp3}`;
|
|
57264
|
-
await this.onerosterNamespace.assessmentResults.upsert(resultId, {
|
|
57265
|
-
sourcedId: resultId,
|
|
57266
|
-
status: ONEROSTER_STATUS.active,
|
|
57267
|
-
assessmentLineItem: { sourcedId: lineItemId },
|
|
57268
|
-
student: { sourcedId: studentId },
|
|
57269
|
-
score,
|
|
57270
|
-
scoreDate: new Date().toISOString(),
|
|
57271
|
-
scoreStatus,
|
|
57272
|
-
inProgress,
|
|
57273
|
-
metadata: {
|
|
57274
|
-
xp,
|
|
57275
|
-
attemptNumber,
|
|
57276
|
-
appName,
|
|
57277
|
-
...totalQuestions !== undefined ? { totalQuestions } : {},
|
|
57278
|
-
...correctQuestions !== undefined ? { correctQuestions } : {},
|
|
57279
|
-
...masteredUnits !== undefined ? { masteredUnits } : {},
|
|
57280
|
-
...pctCompleteApp !== undefined ? { pctCompleteApp } : {}
|
|
57281
|
-
}
|
|
57879
|
+
async getLatestActivityAttempt(studentId, lineItemId, metadataField) {
|
|
57880
|
+
const results = await this.onerosterNamespace.assessmentResults.listOrThrow({
|
|
57881
|
+
filter: `student.sourcedId='${escapeFilterValue(studentId)}' AND assessmentLineItem.sourcedId='${escapeFilterValue(lineItemId)}' AND status='active'`,
|
|
57882
|
+
sort: "dateLastModified",
|
|
57883
|
+
orderBy: "desc",
|
|
57884
|
+
limit: 1
|
|
57282
57885
|
});
|
|
57886
|
+
const latest = results[0];
|
|
57887
|
+
if (!latest) {
|
|
57888
|
+
return null;
|
|
57889
|
+
}
|
|
57890
|
+
return latest.metadata?.[metadataField] || 0;
|
|
57283
57891
|
}
|
|
57284
57892
|
async emitCaliperEvent({
|
|
57285
57893
|
studentId,
|
|
@@ -57294,6 +57902,7 @@ class ProgressRecorder {
|
|
|
57294
57902
|
xpEarned,
|
|
57295
57903
|
masteredUnits,
|
|
57296
57904
|
attemptNumber,
|
|
57905
|
+
objectId,
|
|
57297
57906
|
progressData,
|
|
57298
57907
|
extensions,
|
|
57299
57908
|
runId
|
|
@@ -57311,12 +57920,18 @@ class ProgressRecorder {
|
|
|
57311
57920
|
xpEarned,
|
|
57312
57921
|
masteredUnits,
|
|
57313
57922
|
attemptNumber,
|
|
57923
|
+
objectId,
|
|
57924
|
+
process: true,
|
|
57314
57925
|
subject: progressData.subject,
|
|
57315
57926
|
appName: progressData.appName,
|
|
57316
57927
|
sensorUrl: progressData.sensorUrl,
|
|
57317
57928
|
extensions: extensions || progressData.extensions,
|
|
57318
57929
|
...runId ? { runId } : {}
|
|
57319
|
-
}).catch(
|
|
57930
|
+
}).catch((error) => {
|
|
57931
|
+
setAttributes({ "app.timeback.caliper_emit_failed": true });
|
|
57932
|
+
catchEvent("timeback.caliper_event_failed")(error);
|
|
57933
|
+
throw error;
|
|
57934
|
+
});
|
|
57320
57935
|
}
|
|
57321
57936
|
async emitCourseCompletionHistoryEvent(data) {
|
|
57322
57937
|
await this.caliperNamespace.emitActivityEvent({
|
|
@@ -57494,15 +58109,15 @@ class TimebackClient {
|
|
|
57494
58109
|
masteryTracker;
|
|
57495
58110
|
constructor(config2) {
|
|
57496
58111
|
this.baseUrl = TimebackClient.resolveBaseUrl(config2?.baseUrl);
|
|
57497
|
-
this.environment = process.env[
|
|
58112
|
+
this.environment = process.env[ENV_VARS2.environment] === "staging" ? "staging" : "production";
|
|
57498
58113
|
this.caliperUrl = TimebackClient.resolveCaliperUrl(config2?.caliperUrl, this.environment);
|
|
57499
58114
|
this.authUrl = config2?.credentials?.authUrl;
|
|
57500
58115
|
this.credentials = config2?.credentials;
|
|
57501
58116
|
this.qtiCredentials = config2?.qtiCredentials;
|
|
57502
58117
|
this.options = {
|
|
57503
|
-
retries: config2?.options?.retries ??
|
|
57504
|
-
cacheDuration: config2?.options?.cacheDuration ??
|
|
57505
|
-
timeout: config2?.options?.timeout ??
|
|
58118
|
+
retries: config2?.options?.retries ?? HTTP_DEFAULTS2.retries,
|
|
58119
|
+
cacheDuration: config2?.options?.cacheDuration ?? AUTH_DEFAULTS2.tokenCacheDuration,
|
|
58120
|
+
timeout: config2?.options?.timeout ?? HTTP_DEFAULTS2.timeout,
|
|
57506
58121
|
sensorUrl: config2?.options?.sensorUrl
|
|
57507
58122
|
};
|
|
57508
58123
|
this.oneroster = createOneRosterNamespace(this);
|
|
@@ -57512,7 +58127,7 @@ class TimebackClient {
|
|
|
57512
58127
|
this.cacheManager = new TimebackCacheManager;
|
|
57513
58128
|
this.studentResolver = new StudentResolver(this.cacheManager, this.oneroster);
|
|
57514
58129
|
this.masteryTracker = new MasteryTracker(this.cacheManager, this.oneroster, this.edubridge);
|
|
57515
|
-
this.progressRecorder = new ProgressRecorder(this.studentResolver, this.
|
|
58130
|
+
this.progressRecorder = new ProgressRecorder(this.studentResolver, this.oneroster, this.caliper, this.masteryTracker);
|
|
57516
58131
|
this.sessionRecorder = new SessionRecorder(this.studentResolver, this.caliper);
|
|
57517
58132
|
this.adminEventRecorder = new AdminEventRecorder(this.studentResolver, this.oneroster, this.caliper, this.environment);
|
|
57518
58133
|
if (this.credentials) {
|
|
@@ -57521,16 +58136,16 @@ class TimebackClient {
|
|
|
57521
58136
|
}
|
|
57522
58137
|
static async init(config2) {
|
|
57523
58138
|
let credentials = config2?.credentials;
|
|
57524
|
-
if (!credentials && process.env[
|
|
58139
|
+
if (!credentials && process.env[ENV_VARS2.clientId] && process.env[ENV_VARS2.clientSecret]) {
|
|
57525
58140
|
credentials = {
|
|
57526
|
-
clientId: process.env[
|
|
57527
|
-
clientSecret: process.env[
|
|
58141
|
+
clientId: process.env[ENV_VARS2.clientId],
|
|
58142
|
+
clientSecret: process.env[ENV_VARS2.clientSecret]
|
|
57528
58143
|
};
|
|
57529
58144
|
}
|
|
57530
|
-
const qtiCredentials = config2?.qtiCredentials ?? (process.env[
|
|
57531
|
-
clientId: process.env[
|
|
57532
|
-
clientSecret: process.env[
|
|
57533
|
-
authUrl: process.env[
|
|
58145
|
+
const qtiCredentials = config2?.qtiCredentials ?? (process.env[ENV_VARS2.qtiClientId] && process.env[ENV_VARS2.qtiClientSecret] && process.env[ENV_VARS2.qtiAuthUrl] ? {
|
|
58146
|
+
clientId: process.env[ENV_VARS2.qtiClientId],
|
|
58147
|
+
clientSecret: process.env[ENV_VARS2.qtiClientSecret],
|
|
58148
|
+
authUrl: process.env[ENV_VARS2.qtiAuthUrl]
|
|
57534
58149
|
} : undefined);
|
|
57535
58150
|
return new TimebackClient({
|
|
57536
58151
|
...config2,
|
|
@@ -57543,15 +58158,15 @@ class TimebackClient {
|
|
|
57543
58158
|
if (explicit) {
|
|
57544
58159
|
return explicit;
|
|
57545
58160
|
}
|
|
57546
|
-
const envName = process.env[
|
|
57547
|
-
return
|
|
58161
|
+
const envName = process.env[ENV_VARS2.environment] === "staging" ? "staging" : "production";
|
|
58162
|
+
return TIMEBACK_API_URLS2[envName];
|
|
57548
58163
|
}
|
|
57549
58164
|
static resolveCaliperUrl(input, environment = "production") {
|
|
57550
58165
|
const explicit = (input || "").trim();
|
|
57551
58166
|
if (explicit) {
|
|
57552
58167
|
return explicit;
|
|
57553
58168
|
}
|
|
57554
|
-
return
|
|
58169
|
+
return CALIPER_API_URLS2[environment];
|
|
57555
58170
|
}
|
|
57556
58171
|
getBaseUrl() {
|
|
57557
58172
|
return this.baseUrl;
|
|
@@ -57672,14 +58287,14 @@ class TimebackClient {
|
|
|
57672
58287
|
await this._ensureAuthenticated();
|
|
57673
58288
|
return this.token;
|
|
57674
58289
|
}
|
|
57675
|
-
throw new TimebackAuthenticationError(`QTI credentials are required. Set ${
|
|
58290
|
+
throw new TimebackAuthenticationError(`QTI credentials are required. Set ${ENV_VARS2.qtiClientId}, ${ENV_VARS2.qtiClientSecret}, and ${ENV_VARS2.qtiAuthUrl}.`);
|
|
57676
58291
|
}
|
|
57677
58292
|
async ensureQtiAuthenticated() {
|
|
57678
58293
|
if (this.isQtiAuthenticated()) {
|
|
57679
58294
|
return;
|
|
57680
58295
|
}
|
|
57681
58296
|
if (!this.qtiCredentials) {
|
|
57682
|
-
throw new TimebackAuthenticationError(`QTI credentials are required. Set ${
|
|
58297
|
+
throw new TimebackAuthenticationError(`QTI credentials are required. Set ${ENV_VARS2.qtiClientId}, ${ENV_VARS2.qtiClientSecret}, and ${ENV_VARS2.qtiAuthUrl}.`);
|
|
57683
58298
|
}
|
|
57684
58299
|
try {
|
|
57685
58300
|
const tokenData = await getTimebackTokenResponse({
|
|
@@ -57691,11 +58306,11 @@ class TimebackClient {
|
|
|
57691
58306
|
this.setQtiToken(tokenData.access_token, tokenData.expires_in);
|
|
57692
58307
|
} catch (error) {
|
|
57693
58308
|
const errMsg = errorMessage(error);
|
|
57694
|
-
throw new TimebackAuthenticationError(`QTI authentication failed: ${errMsg}. Verify that ${
|
|
58309
|
+
throw new TimebackAuthenticationError(`QTI authentication failed: ${errMsg}. Verify that ${ENV_VARS2.qtiClientId}, ${ENV_VARS2.qtiClientSecret}, and ${ENV_VARS2.qtiAuthUrl} are correct.`);
|
|
57695
58310
|
}
|
|
57696
58311
|
}
|
|
57697
58312
|
canUseCoreCredentialsForQti() {
|
|
57698
|
-
return this.baseUrl.replace(/\/$/, "") ===
|
|
58313
|
+
return this.baseUrl.replace(/\/$/, "") === TIMEBACK_API_URLS2.production;
|
|
57699
58314
|
}
|
|
57700
58315
|
async resolveStudent(studentIdentifier, providedEmail) {
|
|
57701
58316
|
return this.studentResolver.resolve(studentIdentifier, providedEmail);
|
|
@@ -57716,8 +58331,8 @@ class TimebackClient {
|
|
|
57716
58331
|
await this._ensureAuthenticated();
|
|
57717
58332
|
const edubridgeEnrollments = await this.edubridge.enrollments.listByUser(studentId);
|
|
57718
58333
|
const enrollments = edubridgeEnrollments.map((enrollment) => {
|
|
57719
|
-
const grades = enrollment.course.grades ? enrollment.course.grades.map((g) => parseInt(g, 10)).filter(
|
|
57720
|
-
const subjects = enrollment.course.subjects ? enrollment.course.subjects.filter(
|
|
58334
|
+
const grades = enrollment.course.grades ? enrollment.course.grades.map((g) => parseInt(g, 10)).filter(isTimebackGrade3) : null;
|
|
58335
|
+
const subjects = enrollment.course.subjects ? enrollment.course.subjects.filter(isTimebackSubject3) : null;
|
|
57721
58336
|
return {
|
|
57722
58337
|
sourcedId: enrollment.id,
|
|
57723
58338
|
title: enrollment.course.title,
|
|
@@ -57783,9 +58398,9 @@ class TimebackClient {
|
|
|
57783
58398
|
if (options?.include?.perCourse) {
|
|
57784
58399
|
const gradeStr = enrollment.course.grades?.[0];
|
|
57785
58400
|
const parsedGrade = gradeStr ? parseInt(gradeStr, 10) : 0;
|
|
57786
|
-
const grade =
|
|
58401
|
+
const grade = isTimebackGrade3(parsedGrade) ? parsedGrade : 0;
|
|
57787
58402
|
const subjectStr = enrollment.course.subjects?.[0];
|
|
57788
|
-
const subject = subjectStr &&
|
|
58403
|
+
const subject = subjectStr && isTimebackSubject3(subjectStr) ? subjectStr : "None";
|
|
57789
58404
|
courses.push({
|
|
57790
58405
|
grade,
|
|
57791
58406
|
subject,
|
|
@@ -57859,9 +58474,9 @@ class TimebackClient {
|
|
|
57859
58474
|
if (options?.include?.perCourse) {
|
|
57860
58475
|
const gradeStr = enrollment.course.grades?.[0];
|
|
57861
58476
|
const parsedGrade = gradeStr ? parseInt(gradeStr, 10) : 0;
|
|
57862
|
-
const grade =
|
|
58477
|
+
const grade = isTimebackGrade3(parsedGrade) ? parsedGrade : 0;
|
|
57863
58478
|
const subjectStr = enrollment.course.subjects?.[0];
|
|
57864
|
-
const subject = subjectStr &&
|
|
58479
|
+
const subject = subjectStr && isTimebackSubject3(subjectStr) ? subjectStr : "None";
|
|
57865
58480
|
courses.push({
|
|
57866
58481
|
grade,
|
|
57867
58482
|
subject,
|
|
@@ -57917,6 +58532,12 @@ class TimebackClient {
|
|
|
57917
58532
|
async cleanup(courseId) {
|
|
57918
58533
|
return deleteTimebackResources(this, courseId);
|
|
57919
58534
|
}
|
|
58535
|
+
async deactivateCourse(courseId) {
|
|
58536
|
+
return updateTimebackCourseStatus(this, courseId, "tobedeleted");
|
|
58537
|
+
}
|
|
58538
|
+
async reactivateCourse(courseId) {
|
|
58539
|
+
return updateTimebackCourseStatus(this, courseId, "active");
|
|
58540
|
+
}
|
|
57920
58541
|
}
|
|
57921
58542
|
var __defProp22;
|
|
57922
58543
|
var __export2 = (target, all) => {
|
|
@@ -57928,46 +58549,50 @@ var __export2 = (target, all) => {
|
|
|
57928
58549
|
set: (newValue) => all[name3] = () => newValue
|
|
57929
58550
|
});
|
|
57930
58551
|
};
|
|
57931
|
-
var
|
|
57932
|
-
var
|
|
58552
|
+
var __esm3 = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
58553
|
+
var TIMEBACK_API_URLS2;
|
|
57933
58554
|
var QTI_API_URL = "https://qti.alpha-1edtech.ai/api";
|
|
57934
|
-
var
|
|
57935
|
-
var
|
|
57936
|
-
var
|
|
57937
|
-
var
|
|
57938
|
-
var
|
|
57939
|
-
var
|
|
57940
|
-
var
|
|
57941
|
-
var
|
|
57942
|
-
var
|
|
57943
|
-
var
|
|
57944
|
-
var
|
|
57945
|
-
var
|
|
57946
|
-
var
|
|
57947
|
-
var
|
|
57948
|
-
var
|
|
57949
|
-
var
|
|
57950
|
-
var
|
|
57951
|
-
var
|
|
57952
|
-
var
|
|
57953
|
-
var
|
|
57954
|
-
var
|
|
57955
|
-
var
|
|
57956
|
-
var
|
|
57957
|
-
var
|
|
57958
|
-
var
|
|
57959
|
-
var
|
|
57960
|
-
var
|
|
57961
|
-
var exports_verify;
|
|
57962
|
-
var init_verify;
|
|
58555
|
+
var TIMEBACK_AUTH_URLS2;
|
|
58556
|
+
var CALIPER_API_URLS2;
|
|
58557
|
+
var ONEROSTER_ENDPOINTS2;
|
|
58558
|
+
var QTI_ENDPOINTS2;
|
|
58559
|
+
var CALIPER_ENDPOINTS2;
|
|
58560
|
+
var CALIPER_CONSTANTS2;
|
|
58561
|
+
var TIMEBACK_EVENT_TYPES2;
|
|
58562
|
+
var TIMEBACK_ACTIONS2;
|
|
58563
|
+
var TIMEBACK_TYPES2;
|
|
58564
|
+
var ACTIVITY_METRIC_TYPES2;
|
|
58565
|
+
var TIME_METRIC_TYPES2;
|
|
58566
|
+
var TIMEBACK_SUBJECTS3;
|
|
58567
|
+
var TIMEBACK_GRADE_LEVELS2;
|
|
58568
|
+
var TIMEBACK_GRADE_LEVEL_LABELS2;
|
|
58569
|
+
var CALIPER_SUBJECTS2;
|
|
58570
|
+
var ONEROSTER_STATUS2;
|
|
58571
|
+
var SCORE_STATUS2;
|
|
58572
|
+
var ENV_VARS2;
|
|
58573
|
+
var HTTP_DEFAULTS2;
|
|
58574
|
+
var AUTH_DEFAULTS2;
|
|
58575
|
+
var CACHE_DEFAULTS2;
|
|
58576
|
+
var CONFIG_DEFAULTS2;
|
|
58577
|
+
var PLAYCADEMY_DEFAULTS2;
|
|
58578
|
+
var RESOURCE_DEFAULTS2;
|
|
58579
|
+
var HTTP_STATUS2;
|
|
58580
|
+
var ERROR_NAMES2;
|
|
58581
|
+
var init_constants5;
|
|
57963
58582
|
var TimebackError;
|
|
57964
58583
|
var TimebackApiError;
|
|
57965
58584
|
var TimebackAuthenticationError;
|
|
57966
58585
|
var StudentNotFoundError;
|
|
57967
58586
|
var ConfigurationError;
|
|
58587
|
+
var ResourceAlreadyExistsError;
|
|
57968
58588
|
var ResourceNotFoundError;
|
|
57969
|
-
var
|
|
57970
|
-
var
|
|
58589
|
+
var init_errors4;
|
|
58590
|
+
var UUID_REGEX2;
|
|
58591
|
+
var init_ids;
|
|
58592
|
+
var exports_verify;
|
|
58593
|
+
var init_verify;
|
|
58594
|
+
var SUBJECT_VALUES2;
|
|
58595
|
+
var GRADE_VALUES2;
|
|
57971
58596
|
var TimebackAuthError;
|
|
57972
58597
|
var UUID_PATTERN;
|
|
57973
58598
|
var storage;
|
|
@@ -58000,320 +58625,7 @@ var init_dist2 = __esm(() => {
|
|
|
58000
58625
|
init_spans();
|
|
58001
58626
|
init_esm();
|
|
58002
58627
|
__defProp22 = Object.defineProperty;
|
|
58003
|
-
|
|
58004
|
-
TIMEBACK_API_URLS = {
|
|
58005
|
-
production: "https://api.alpha-1edtech.ai",
|
|
58006
|
-
staging: "https://api.staging.alpha-1edtech.com"
|
|
58007
|
-
};
|
|
58008
|
-
TIMEBACK_AUTH_URLS = {
|
|
58009
|
-
production: "https://prod-beyond-timeback-api-2-idp.auth.us-east-1.amazoncognito.com",
|
|
58010
|
-
staging: "https://alpha-auth-development-idp.auth.us-west-2.amazoncognito.com"
|
|
58011
|
-
};
|
|
58012
|
-
CALIPER_API_URLS = {
|
|
58013
|
-
production: "https://caliper.alpha-1edtech.ai",
|
|
58014
|
-
staging: "https://caliper-staging.alpha-1edtech.com"
|
|
58015
|
-
};
|
|
58016
|
-
ONEROSTER_ENDPOINTS = {
|
|
58017
|
-
organizations: "/ims/oneroster/rostering/v1p2/orgs",
|
|
58018
|
-
courses: "/ims/oneroster/rostering/v1p2/courses",
|
|
58019
|
-
courseComponents: "/ims/oneroster/rostering/v1p2/courses/components",
|
|
58020
|
-
resources: "/ims/oneroster/resources/v1p2/resources",
|
|
58021
|
-
componentResources: "/ims/oneroster/rostering/v1p2/courses/component-resources",
|
|
58022
|
-
classes: "/ims/oneroster/rostering/v1p2/classes",
|
|
58023
|
-
enrollments: "/ims/oneroster/rostering/v1p2/enrollments",
|
|
58024
|
-
assessmentLineItems: "/ims/oneroster/gradebook/v1p2/assessmentLineItems",
|
|
58025
|
-
assessmentResults: "/ims/oneroster/gradebook/v1p2/assessmentResults",
|
|
58026
|
-
users: "/ims/oneroster/rostering/v1p2/users"
|
|
58027
|
-
};
|
|
58028
|
-
QTI_ENDPOINTS = {
|
|
58029
|
-
assessmentTests: "/assessment-tests",
|
|
58030
|
-
assessmentItems: "/assessment-items"
|
|
58031
|
-
};
|
|
58032
|
-
CALIPER_ENDPOINTS = {
|
|
58033
|
-
event: "/caliper/event",
|
|
58034
|
-
events: "/caliper/events",
|
|
58035
|
-
validate: "/caliper/event/validate"
|
|
58036
|
-
};
|
|
58037
|
-
CALIPER_CONSTANTS = {
|
|
58038
|
-
context: "http://purl.imsglobal.org/ctx/caliper/v1p2",
|
|
58039
|
-
profile: "TimebackProfile",
|
|
58040
|
-
dataVersion: "http://purl.imsglobal.org/ctx/caliper/v1p2"
|
|
58041
|
-
};
|
|
58042
|
-
TIMEBACK_EVENT_TYPES = {
|
|
58043
|
-
activityEvent: "ActivityEvent",
|
|
58044
|
-
timeSpentEvent: "TimeSpentEvent"
|
|
58045
|
-
};
|
|
58046
|
-
TIMEBACK_ACTIONS = {
|
|
58047
|
-
completed: "Completed",
|
|
58048
|
-
spentTime: "SpentTime"
|
|
58049
|
-
};
|
|
58050
|
-
TIMEBACK_TYPES = {
|
|
58051
|
-
user: "TimebackUser",
|
|
58052
|
-
activityContext: "TimebackActivityContext",
|
|
58053
|
-
activityMetricsCollection: "TimebackActivityMetricsCollection",
|
|
58054
|
-
timeSpentMetricsCollection: "TimebackTimeSpentMetricsCollection"
|
|
58055
|
-
};
|
|
58056
|
-
ACTIVITY_METRIC_TYPES = {
|
|
58057
|
-
totalQuestions: "totalQuestions",
|
|
58058
|
-
correctQuestions: "correctQuestions",
|
|
58059
|
-
xpEarned: "xpEarned",
|
|
58060
|
-
masteredUnits: "masteredUnits"
|
|
58061
|
-
};
|
|
58062
|
-
TIME_METRIC_TYPES = {
|
|
58063
|
-
active: "active",
|
|
58064
|
-
inactive: "inactive",
|
|
58065
|
-
waste: "waste",
|
|
58066
|
-
unknown: "unknown",
|
|
58067
|
-
antiPattern: "anti-pattern"
|
|
58068
|
-
};
|
|
58069
|
-
TIMEBACK_SUBJECTS2 = [
|
|
58070
|
-
"Math",
|
|
58071
|
-
"FastMath",
|
|
58072
|
-
"Science",
|
|
58073
|
-
"Social Studies",
|
|
58074
|
-
"Language",
|
|
58075
|
-
"Reading",
|
|
58076
|
-
"Vocabulary",
|
|
58077
|
-
"Writing"
|
|
58078
|
-
];
|
|
58079
|
-
TIMEBACK_GRADE_LEVELS = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
|
|
58080
|
-
TIMEBACK_GRADE_LEVEL_LABELS = {
|
|
58081
|
-
"-1": "pre-k",
|
|
58082
|
-
"0": "kindergarten",
|
|
58083
|
-
"1": "1st grade",
|
|
58084
|
-
"2": "2nd grade",
|
|
58085
|
-
"3": "3rd grade",
|
|
58086
|
-
"4": "4th grade",
|
|
58087
|
-
"5": "5th grade",
|
|
58088
|
-
"6": "6th grade",
|
|
58089
|
-
"7": "7th grade",
|
|
58090
|
-
"8": "8th grade",
|
|
58091
|
-
"9": "9th grade",
|
|
58092
|
-
"10": "10th grade",
|
|
58093
|
-
"11": "11th grade",
|
|
58094
|
-
"12": "12th grade",
|
|
58095
|
-
"13": "AP"
|
|
58096
|
-
};
|
|
58097
|
-
CALIPER_SUBJECTS = {
|
|
58098
|
-
Reading: "Reading",
|
|
58099
|
-
Language: "Language",
|
|
58100
|
-
Vocabulary: "Vocabulary",
|
|
58101
|
-
SocialStudies: "Social Studies",
|
|
58102
|
-
Writing: "Writing",
|
|
58103
|
-
Science: "Science",
|
|
58104
|
-
FastMath: "FastMath",
|
|
58105
|
-
Math: "Math",
|
|
58106
|
-
None: "None"
|
|
58107
|
-
};
|
|
58108
|
-
ONEROSTER_STATUS = {
|
|
58109
|
-
active: "active",
|
|
58110
|
-
toBeDeleted: "tobedeleted"
|
|
58111
|
-
};
|
|
58112
|
-
SCORE_STATUS = {
|
|
58113
|
-
exempt: "exempt",
|
|
58114
|
-
fullyGraded: "fully graded",
|
|
58115
|
-
notSubmitted: "not submitted",
|
|
58116
|
-
partiallyGraded: "partially graded",
|
|
58117
|
-
submitted: "submitted"
|
|
58118
|
-
};
|
|
58119
|
-
ENV_VARS = {
|
|
58120
|
-
clientId: "TIMEBACK_CLIENT_ID",
|
|
58121
|
-
clientSecret: "TIMEBACK_CLIENT_SECRET",
|
|
58122
|
-
baseUrl: "TIMEBACK_BASE_URL",
|
|
58123
|
-
environment: "TIMEBACK_ENVIRONMENT",
|
|
58124
|
-
vendorResourceId: "TIMEBACK_VENDOR_RESOURCE_ID",
|
|
58125
|
-
launchBaseUrl: "GAME_URL",
|
|
58126
|
-
qtiClientId: "QTI_CLIENT_ID",
|
|
58127
|
-
qtiClientSecret: "QTI_CLIENT_SECRET",
|
|
58128
|
-
qtiAuthUrl: "QTI_AUTH_URL"
|
|
58129
|
-
};
|
|
58130
|
-
HTTP_DEFAULTS = {
|
|
58131
|
-
timeout: 30000,
|
|
58132
|
-
retries: 3,
|
|
58133
|
-
retryBackoffBase: 2
|
|
58134
|
-
};
|
|
58135
|
-
AUTH_DEFAULTS = {
|
|
58136
|
-
tokenCacheDuration: 50000
|
|
58137
|
-
};
|
|
58138
|
-
CACHE_DEFAULTS = {
|
|
58139
|
-
defaultTTL: 600000,
|
|
58140
|
-
defaultMaxSize: 500,
|
|
58141
|
-
defaultName: "TimebackCache",
|
|
58142
|
-
studentTTL: 600000,
|
|
58143
|
-
studentMaxSize: 500,
|
|
58144
|
-
assessmentTTL: 1800000,
|
|
58145
|
-
assessmentMaxSize: 200,
|
|
58146
|
-
enrollmentTTL: 5000,
|
|
58147
|
-
enrollmentMaxSize: 100
|
|
58148
|
-
};
|
|
58149
|
-
CONFIG_DEFAULTS = {
|
|
58150
|
-
fileNames: ["timeback.config.js", "timeback.config.json"]
|
|
58151
|
-
};
|
|
58152
|
-
PLAYCADEMY_DEFAULTS = {
|
|
58153
|
-
organization: TIMEBACK_ORG_SOURCED_ID,
|
|
58154
|
-
launchBaseUrls: PLAYCADEMY_BASE_URLS
|
|
58155
|
-
};
|
|
58156
|
-
RESOURCE_DEFAULTS = {
|
|
58157
|
-
organization: {
|
|
58158
|
-
name: TIMEBACK_ORG_NAME,
|
|
58159
|
-
type: TIMEBACK_ORG_TYPE
|
|
58160
|
-
},
|
|
58161
|
-
course: {
|
|
58162
|
-
gradingScheme: TIMEBACK_COURSE_DEFAULTS.gradingScheme,
|
|
58163
|
-
level: TIMEBACK_COURSE_DEFAULTS.level,
|
|
58164
|
-
metadata: {
|
|
58165
|
-
goals: TIMEBACK_COURSE_DEFAULTS.goals,
|
|
58166
|
-
metrics: TIMEBACK_COURSE_DEFAULTS.metrics
|
|
58167
|
-
}
|
|
58168
|
-
},
|
|
58169
|
-
component: TIMEBACK_COMPONENT_DEFAULTS,
|
|
58170
|
-
resource: TIMEBACK_RESOURCE_DEFAULTS,
|
|
58171
|
-
componentResource: TIMEBACK_COMPONENT_RESOURCE_DEFAULTS
|
|
58172
|
-
};
|
|
58173
|
-
HTTP_STATUS = {
|
|
58174
|
-
CLIENT_ERROR_MIN: 400,
|
|
58175
|
-
CLIENT_ERROR_MAX: 500,
|
|
58176
|
-
SERVER_ERROR_MIN: 500
|
|
58177
|
-
};
|
|
58178
|
-
ERROR_NAMES = {
|
|
58179
|
-
timebackAuth: "TimebackAuthError",
|
|
58180
|
-
timebackApi: "TimebackApiError",
|
|
58181
|
-
timebackConfig: "TimebackConfigError",
|
|
58182
|
-
timebackSdk: "TimebackSDKError"
|
|
58183
|
-
};
|
|
58184
|
-
});
|
|
58185
|
-
exports_verify = {};
|
|
58186
|
-
__export2(exports_verify, {
|
|
58187
|
-
verifyTimebackResources: () => verifyTimebackResources,
|
|
58188
|
-
fetchTimebackConfig: () => fetchTimebackConfig
|
|
58189
|
-
});
|
|
58190
|
-
init_verify = __esm2(() => {
|
|
58191
|
-
init_constants4();
|
|
58192
|
-
});
|
|
58193
|
-
init_constants4();
|
|
58194
|
-
TimebackError = class TimebackError2 extends Error {
|
|
58195
|
-
constructor(message) {
|
|
58196
|
-
super(message);
|
|
58197
|
-
this.name = ERROR_NAMES.timebackSdk;
|
|
58198
|
-
}
|
|
58199
|
-
};
|
|
58200
|
-
TimebackApiError = class TimebackApiError2 extends Error {
|
|
58201
|
-
status;
|
|
58202
|
-
details;
|
|
58203
|
-
constructor(status, message, details) {
|
|
58204
|
-
super(`${status} ${message}`);
|
|
58205
|
-
this.name = ERROR_NAMES.timebackApi;
|
|
58206
|
-
this.status = status;
|
|
58207
|
-
this.details = details;
|
|
58208
|
-
Object.setPrototypeOf(this, TimebackApiError2.prototype);
|
|
58209
|
-
}
|
|
58210
|
-
};
|
|
58211
|
-
TimebackAuthenticationError = class TimebackAuthenticationError2 extends TimebackError {
|
|
58212
|
-
constructor(message) {
|
|
58213
|
-
super(message || "Authentication failed. Please verify TIMEBACK_CLIENT_ID and TIMEBACK_CLIENT_SECRET are set correctly.");
|
|
58214
|
-
this.name = "TimebackAuthenticationError";
|
|
58215
|
-
Object.setPrototypeOf(this, TimebackAuthenticationError2.prototype);
|
|
58216
|
-
}
|
|
58217
|
-
};
|
|
58218
|
-
StudentNotFoundError = class StudentNotFoundError2 extends TimebackError {
|
|
58219
|
-
identifier;
|
|
58220
|
-
identifierType;
|
|
58221
|
-
constructor(identifier, identifierType = "email") {
|
|
58222
|
-
super(`Student not found with ${identifierType}: ${identifier}. Ensure the student exists in OneRoster. If this is a new student, they must be created in OneRoster first.`);
|
|
58223
|
-
this.name = "StudentNotFoundError";
|
|
58224
|
-
this.identifier = identifier;
|
|
58225
|
-
this.identifierType = identifierType;
|
|
58226
|
-
Object.setPrototypeOf(this, StudentNotFoundError2.prototype);
|
|
58227
|
-
}
|
|
58228
|
-
};
|
|
58229
|
-
ConfigurationError = class ConfigurationError2 extends TimebackError {
|
|
58230
|
-
field;
|
|
58231
|
-
constructor(field, message) {
|
|
58232
|
-
super(message || `Missing required configuration: ${field}. Please check your timeback.config.js file.`);
|
|
58233
|
-
this.name = "ConfigurationError";
|
|
58234
|
-
this.field = field;
|
|
58235
|
-
Object.setPrototypeOf(this, ConfigurationError2.prototype);
|
|
58236
|
-
}
|
|
58237
|
-
};
|
|
58238
|
-
ResourceNotFoundError = class ResourceNotFoundError2 extends TimebackError {
|
|
58239
|
-
resourceType;
|
|
58240
|
-
sourcedId;
|
|
58241
|
-
constructor(resourceType, sourcedId) {
|
|
58242
|
-
const message = sourcedId === "setup-not-run" ? `TimeBack resources have not been created yet. Please run 'bunx @playcademy/timeback setup' to create the required resources (organization, course, components, etc.) before recording progress.` : `${resourceType} with ID '${sourcedId}' not found`;
|
|
58243
|
-
super(message);
|
|
58244
|
-
this.name = "ResourceNotFoundError";
|
|
58245
|
-
this.resourceType = resourceType;
|
|
58246
|
-
this.sourcedId = sourcedId;
|
|
58247
|
-
Object.setPrototypeOf(this, ResourceNotFoundError2.prototype);
|
|
58248
|
-
}
|
|
58249
|
-
};
|
|
58250
|
-
init_constants4();
|
|
58251
|
-
SUBJECT_VALUES = TIMEBACK_SUBJECTS2;
|
|
58252
|
-
GRADE_VALUES = TIMEBACK_GRADE_LEVELS;
|
|
58253
|
-
init_verify();
|
|
58254
|
-
init_constants4();
|
|
58255
|
-
init_constants4();
|
|
58256
|
-
if (process.env.DEBUG === "true") {
|
|
58257
|
-
process.env.TERM = "dumb";
|
|
58258
|
-
}
|
|
58259
|
-
TimebackAuthError = class TimebackAuthError2 extends Error {
|
|
58260
|
-
statusCode;
|
|
58261
|
-
constructor(message, statusCode) {
|
|
58262
|
-
super(message);
|
|
58263
|
-
this.name = ERROR_NAMES.timebackAuth;
|
|
58264
|
-
this.statusCode = statusCode;
|
|
58265
|
-
}
|
|
58266
|
-
};
|
|
58267
|
-
init_constants4();
|
|
58268
|
-
UUID_PATTERN = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi;
|
|
58269
|
-
storage = new AsyncLocalStorage;
|
|
58270
|
-
init_constants4();
|
|
58271
|
-
init_constants4();
|
|
58272
|
-
init_constants4();
|
|
58273
|
-
init_constants4();
|
|
58274
|
-
init_constants4();
|
|
58275
|
-
init_constants4();
|
|
58276
|
-
init_constants4();
|
|
58277
|
-
init_constants4();
|
|
58278
|
-
EmailSchema = exports_external.string().email();
|
|
58279
|
-
StudentSourcedIdSchema = exports_external.string().min(1, {
|
|
58280
|
-
message: "Student sourcedId must be a non-empty string"
|
|
58281
|
-
});
|
|
58282
|
-
StudentIdentifierSchema = exports_external.union([EmailSchema, StudentSourcedIdSchema]);
|
|
58283
|
-
});
|
|
58284
|
-
var __esm3 = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
58285
|
-
var TIMEBACK_API_URLS2;
|
|
58286
|
-
var QTI_API_URL2 = "https://qti.alpha-1edtech.ai/api";
|
|
58287
|
-
var TIMEBACK_AUTH_URLS2;
|
|
58288
|
-
var CALIPER_API_URLS2;
|
|
58289
|
-
var ONEROSTER_ENDPOINTS2;
|
|
58290
|
-
var QTI_ENDPOINTS2;
|
|
58291
|
-
var CALIPER_ENDPOINTS2;
|
|
58292
|
-
var CALIPER_CONSTANTS2;
|
|
58293
|
-
var TIMEBACK_EVENT_TYPES2;
|
|
58294
|
-
var TIMEBACK_ACTIONS2;
|
|
58295
|
-
var TIMEBACK_TYPES2;
|
|
58296
|
-
var ACTIVITY_METRIC_TYPES2;
|
|
58297
|
-
var TIME_METRIC_TYPES2;
|
|
58298
|
-
var TIMEBACK_SUBJECTS3;
|
|
58299
|
-
var TIMEBACK_GRADE_LEVELS2;
|
|
58300
|
-
var TIMEBACK_GRADE_LEVEL_LABELS2;
|
|
58301
|
-
var CALIPER_SUBJECTS2;
|
|
58302
|
-
var ONEROSTER_STATUS2;
|
|
58303
|
-
var SCORE_STATUS2;
|
|
58304
|
-
var ENV_VARS2;
|
|
58305
|
-
var HTTP_DEFAULTS2;
|
|
58306
|
-
var AUTH_DEFAULTS2;
|
|
58307
|
-
var CACHE_DEFAULTS2;
|
|
58308
|
-
var CONFIG_DEFAULTS2;
|
|
58309
|
-
var PLAYCADEMY_DEFAULTS2;
|
|
58310
|
-
var RESOURCE_DEFAULTS2;
|
|
58311
|
-
var HTTP_STATUS2;
|
|
58312
|
-
var ERROR_NAMES2;
|
|
58313
|
-
var init_constants6;
|
|
58314
|
-
var init_constants5 = __esm(() => {
|
|
58315
|
-
init_src();
|
|
58316
|
-
init_constants6 = __esm3(() => {
|
|
58628
|
+
init_constants5 = __esm3(() => {
|
|
58317
58629
|
TIMEBACK_API_URLS2 = {
|
|
58318
58630
|
production: "https://api.alpha-1edtech.ai",
|
|
58319
58631
|
staging: "https://api.staging.alpha-1edtech.com"
|
|
@@ -58495,34 +58807,152 @@ var init_constants5 = __esm(() => {
|
|
|
58495
58807
|
timebackSdk: "TimebackSDKError"
|
|
58496
58808
|
};
|
|
58497
58809
|
});
|
|
58498
|
-
|
|
58810
|
+
init_errors4 = __esm3(() => {
|
|
58811
|
+
init_constants5();
|
|
58812
|
+
TimebackError = class TimebackError2 extends Error {
|
|
58813
|
+
constructor(message) {
|
|
58814
|
+
super(message);
|
|
58815
|
+
this.name = ERROR_NAMES2.timebackSdk;
|
|
58816
|
+
}
|
|
58817
|
+
};
|
|
58818
|
+
TimebackApiError = class TimebackApiError2 extends Error {
|
|
58819
|
+
status;
|
|
58820
|
+
details;
|
|
58821
|
+
constructor(status, message, details) {
|
|
58822
|
+
super(`${status} ${message}`);
|
|
58823
|
+
this.name = ERROR_NAMES2.timebackApi;
|
|
58824
|
+
this.status = status;
|
|
58825
|
+
this.details = details;
|
|
58826
|
+
Object.setPrototypeOf(this, TimebackApiError2.prototype);
|
|
58827
|
+
}
|
|
58828
|
+
};
|
|
58829
|
+
TimebackAuthenticationError = class TimebackAuthenticationError2 extends TimebackError {
|
|
58830
|
+
constructor(message) {
|
|
58831
|
+
super(message || "Authentication failed. Please verify TIMEBACK_CLIENT_ID and TIMEBACK_CLIENT_SECRET are set correctly.");
|
|
58832
|
+
this.name = "TimebackAuthenticationError";
|
|
58833
|
+
Object.setPrototypeOf(this, TimebackAuthenticationError2.prototype);
|
|
58834
|
+
}
|
|
58835
|
+
};
|
|
58836
|
+
StudentNotFoundError = class StudentNotFoundError2 extends TimebackError {
|
|
58837
|
+
identifier;
|
|
58838
|
+
identifierType;
|
|
58839
|
+
constructor(identifier, identifierType = "email") {
|
|
58840
|
+
super(`Student not found with ${identifierType}: ${identifier}. Ensure the student exists in OneRoster. If this is a new student, they must be created in OneRoster first.`);
|
|
58841
|
+
this.name = "StudentNotFoundError";
|
|
58842
|
+
this.identifier = identifier;
|
|
58843
|
+
this.identifierType = identifierType;
|
|
58844
|
+
Object.setPrototypeOf(this, StudentNotFoundError2.prototype);
|
|
58845
|
+
}
|
|
58846
|
+
};
|
|
58847
|
+
ConfigurationError = class ConfigurationError2 extends TimebackError {
|
|
58848
|
+
field;
|
|
58849
|
+
constructor(field, message) {
|
|
58850
|
+
super(message || `Missing required configuration: ${field}. Please check your timeback.config.js file.`);
|
|
58851
|
+
this.name = "ConfigurationError";
|
|
58852
|
+
this.field = field;
|
|
58853
|
+
Object.setPrototypeOf(this, ConfigurationError2.prototype);
|
|
58854
|
+
}
|
|
58855
|
+
};
|
|
58856
|
+
ResourceAlreadyExistsError = class ResourceAlreadyExistsError2 extends TimebackError {
|
|
58857
|
+
resourceType;
|
|
58858
|
+
sourcedId;
|
|
58859
|
+
originalError;
|
|
58860
|
+
constructor(resourceType, sourcedId, originalError) {
|
|
58861
|
+
super(`${resourceType} with ID '${sourcedId}' already exists`);
|
|
58862
|
+
this.name = "ResourceAlreadyExistsError";
|
|
58863
|
+
this.resourceType = resourceType;
|
|
58864
|
+
this.sourcedId = sourcedId;
|
|
58865
|
+
this.originalError = originalError;
|
|
58866
|
+
Object.setPrototypeOf(this, ResourceAlreadyExistsError2.prototype);
|
|
58867
|
+
}
|
|
58868
|
+
};
|
|
58869
|
+
ResourceNotFoundError = class ResourceNotFoundError2 extends TimebackError {
|
|
58870
|
+
resourceType;
|
|
58871
|
+
sourcedId;
|
|
58872
|
+
constructor(resourceType, sourcedId) {
|
|
58873
|
+
const message = sourcedId === "setup-not-run" ? `TimeBack resources have not been created yet. Please run 'bunx @playcademy/timeback setup' to create the required resources (organization, course, components, etc.) before recording progress.` : `${resourceType} with ID '${sourcedId}' not found`;
|
|
58874
|
+
super(message);
|
|
58875
|
+
this.name = "ResourceNotFoundError";
|
|
58876
|
+
this.resourceType = resourceType;
|
|
58877
|
+
this.sourcedId = sourcedId;
|
|
58878
|
+
Object.setPrototypeOf(this, ResourceNotFoundError2.prototype);
|
|
58879
|
+
}
|
|
58880
|
+
};
|
|
58881
|
+
});
|
|
58882
|
+
init_ids = __esm3(() => {
|
|
58883
|
+
init_errors4();
|
|
58884
|
+
UUID_REGEX2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
58885
|
+
});
|
|
58886
|
+
exports_verify = {};
|
|
58887
|
+
__export2(exports_verify, {
|
|
58888
|
+
verifyTimebackResources: () => verifyTimebackResources,
|
|
58889
|
+
fetchTimebackConfig: () => fetchTimebackConfig
|
|
58890
|
+
});
|
|
58891
|
+
init_verify = __esm3(() => {
|
|
58892
|
+
init_constants5();
|
|
58893
|
+
init_ids();
|
|
58894
|
+
});
|
|
58895
|
+
init_constants5();
|
|
58896
|
+
SUBJECT_VALUES2 = TIMEBACK_SUBJECTS3;
|
|
58897
|
+
GRADE_VALUES2 = TIMEBACK_GRADE_LEVELS2;
|
|
58898
|
+
init_ids();
|
|
58899
|
+
init_ids();
|
|
58900
|
+
init_verify();
|
|
58901
|
+
init_ids();
|
|
58902
|
+
init_constants5();
|
|
58903
|
+
init_errors4();
|
|
58904
|
+
init_constants5();
|
|
58905
|
+
if (process.env.DEBUG === "true") {
|
|
58906
|
+
process.env.TERM = "dumb";
|
|
58907
|
+
}
|
|
58908
|
+
TimebackAuthError = class TimebackAuthError2 extends Error {
|
|
58909
|
+
statusCode;
|
|
58910
|
+
constructor(message, statusCode) {
|
|
58911
|
+
super(message);
|
|
58912
|
+
this.name = ERROR_NAMES2.timebackAuth;
|
|
58913
|
+
this.statusCode = statusCode;
|
|
58914
|
+
}
|
|
58915
|
+
};
|
|
58916
|
+
init_ids();
|
|
58917
|
+
init_constants5();
|
|
58918
|
+
init_errors4();
|
|
58919
|
+
UUID_PATTERN = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi;
|
|
58920
|
+
storage = new AsyncLocalStorage;
|
|
58921
|
+
init_constants5();
|
|
58922
|
+
init_constants5();
|
|
58923
|
+
init_errors4();
|
|
58924
|
+
init_constants5();
|
|
58925
|
+
init_constants5();
|
|
58926
|
+
init_constants5();
|
|
58927
|
+
init_constants5();
|
|
58928
|
+
init_constants5();
|
|
58929
|
+
init_ids();
|
|
58930
|
+
init_ids();
|
|
58931
|
+
init_errors4();
|
|
58932
|
+
init_ids();
|
|
58933
|
+
init_errors4();
|
|
58934
|
+
EmailSchema = exports_external.string().email();
|
|
58935
|
+
StudentSourcedIdSchema = exports_external.string().min(1, {
|
|
58936
|
+
message: "Student sourcedId must be a non-empty string"
|
|
58937
|
+
});
|
|
58938
|
+
StudentIdentifierSchema = exports_external.union([EmailSchema, StudentSourcedIdSchema]);
|
|
58939
|
+
init_ids();
|
|
58499
58940
|
});
|
|
58500
|
-
function
|
|
58501
|
-
|
|
58502
|
-
|
|
58503
|
-
function isCourseMetadata(value) {
|
|
58504
|
-
return isObject2(value);
|
|
58505
|
-
}
|
|
58506
|
-
function isResourceMetadata(value) {
|
|
58507
|
-
return isObject2(value);
|
|
58508
|
-
}
|
|
58509
|
-
function isPlaycademyResourceMetadata2(value) {
|
|
58510
|
-
if (!isObject2(value)) {
|
|
58511
|
-
return false;
|
|
58941
|
+
function deriveTimebackCourseLevelFromGrade(grade) {
|
|
58942
|
+
if (grade === 13) {
|
|
58943
|
+
return TIMEBACK_COURSE_DEFAULTS.level.ap;
|
|
58512
58944
|
}
|
|
58513
|
-
if (
|
|
58514
|
-
return
|
|
58945
|
+
if (grade <= 5) {
|
|
58946
|
+
return TIMEBACK_COURSE_DEFAULTS.level.elementary;
|
|
58515
58947
|
}
|
|
58516
|
-
|
|
58517
|
-
|
|
58518
|
-
|
|
58519
|
-
return
|
|
58520
|
-
}
|
|
58521
|
-
function isTimebackGrade3(value) {
|
|
58522
|
-
return typeof value === "number" && Number.isInteger(value) && GRADE_VALUES2.includes(value);
|
|
58948
|
+
if (grade <= 8) {
|
|
58949
|
+
return TIMEBACK_COURSE_DEFAULTS.level.middle;
|
|
58950
|
+
}
|
|
58951
|
+
return TIMEBACK_COURSE_DEFAULTS.level.high;
|
|
58523
58952
|
}
|
|
58524
58953
|
var __esm4 = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
58525
58954
|
var TIMEBACK_API_URLS3;
|
|
58955
|
+
var QTI_API_URL2 = "https://qti.alpha-1edtech.ai/api";
|
|
58526
58956
|
var TIMEBACK_AUTH_URLS3;
|
|
58527
58957
|
var CALIPER_API_URLS3;
|
|
58528
58958
|
var ONEROSTER_ENDPOINTS3;
|
|
@@ -58550,9 +58980,7 @@ var RESOURCE_DEFAULTS3;
|
|
|
58550
58980
|
var HTTP_STATUS3;
|
|
58551
58981
|
var ERROR_NAMES3;
|
|
58552
58982
|
var init_constants7;
|
|
58553
|
-
var
|
|
58554
|
-
var GRADE_VALUES2;
|
|
58555
|
-
var init_types3 = __esm(() => {
|
|
58983
|
+
var init_constants6 = __esm(() => {
|
|
58556
58984
|
init_src();
|
|
58557
58985
|
init_constants7 = __esm4(() => {
|
|
58558
58986
|
TIMEBACK_API_URLS3 = {
|
|
@@ -58737,8 +59165,6 @@ var init_types3 = __esm(() => {
|
|
|
58737
59165
|
};
|
|
58738
59166
|
});
|
|
58739
59167
|
init_constants7();
|
|
58740
|
-
SUBJECT_VALUES2 = TIMEBACK_SUBJECTS4;
|
|
58741
|
-
GRADE_VALUES2 = TIMEBACK_GRADE_LEVELS3;
|
|
58742
59168
|
});
|
|
58743
59169
|
function deriveSourcedIds2(courseId) {
|
|
58744
59170
|
return {
|
|
@@ -58787,6 +59213,16 @@ var RESOURCE_DEFAULTS4;
|
|
|
58787
59213
|
var HTTP_STATUS4;
|
|
58788
59214
|
var ERROR_NAMES4;
|
|
58789
59215
|
var init_constants8;
|
|
59216
|
+
var TimebackError2;
|
|
59217
|
+
var TimebackApiError2;
|
|
59218
|
+
var TimebackAuthenticationError2;
|
|
59219
|
+
var StudentNotFoundError2;
|
|
59220
|
+
var ConfigurationError2;
|
|
59221
|
+
var ResourceAlreadyExistsError2;
|
|
59222
|
+
var ResourceNotFoundError2;
|
|
59223
|
+
var init_errors5;
|
|
59224
|
+
var UUID_REGEX3;
|
|
59225
|
+
var init_ids2;
|
|
58790
59226
|
var init_utils6 = __esm(() => {
|
|
58791
59227
|
init_src();
|
|
58792
59228
|
init_constants8 = __esm5(() => {
|
|
@@ -58971,6 +59407,82 @@ var init_utils6 = __esm(() => {
|
|
|
58971
59407
|
timebackSdk: "TimebackSDKError"
|
|
58972
59408
|
};
|
|
58973
59409
|
});
|
|
59410
|
+
init_errors5 = __esm5(() => {
|
|
59411
|
+
init_constants8();
|
|
59412
|
+
TimebackError2 = class TimebackError3 extends Error {
|
|
59413
|
+
constructor(message) {
|
|
59414
|
+
super(message);
|
|
59415
|
+
this.name = ERROR_NAMES4.timebackSdk;
|
|
59416
|
+
}
|
|
59417
|
+
};
|
|
59418
|
+
TimebackApiError2 = class TimebackApiError3 extends Error {
|
|
59419
|
+
status;
|
|
59420
|
+
details;
|
|
59421
|
+
constructor(status, message, details) {
|
|
59422
|
+
super(`${status} ${message}`);
|
|
59423
|
+
this.name = ERROR_NAMES4.timebackApi;
|
|
59424
|
+
this.status = status;
|
|
59425
|
+
this.details = details;
|
|
59426
|
+
Object.setPrototypeOf(this, TimebackApiError3.prototype);
|
|
59427
|
+
}
|
|
59428
|
+
};
|
|
59429
|
+
TimebackAuthenticationError2 = class TimebackAuthenticationError3 extends TimebackError2 {
|
|
59430
|
+
constructor(message) {
|
|
59431
|
+
super(message || "Authentication failed. Please verify TIMEBACK_CLIENT_ID and TIMEBACK_CLIENT_SECRET are set correctly.");
|
|
59432
|
+
this.name = "TimebackAuthenticationError";
|
|
59433
|
+
Object.setPrototypeOf(this, TimebackAuthenticationError3.prototype);
|
|
59434
|
+
}
|
|
59435
|
+
};
|
|
59436
|
+
StudentNotFoundError2 = class StudentNotFoundError3 extends TimebackError2 {
|
|
59437
|
+
identifier;
|
|
59438
|
+
identifierType;
|
|
59439
|
+
constructor(identifier, identifierType = "email") {
|
|
59440
|
+
super(`Student not found with ${identifierType}: ${identifier}. Ensure the student exists in OneRoster. If this is a new student, they must be created in OneRoster first.`);
|
|
59441
|
+
this.name = "StudentNotFoundError";
|
|
59442
|
+
this.identifier = identifier;
|
|
59443
|
+
this.identifierType = identifierType;
|
|
59444
|
+
Object.setPrototypeOf(this, StudentNotFoundError3.prototype);
|
|
59445
|
+
}
|
|
59446
|
+
};
|
|
59447
|
+
ConfigurationError2 = class ConfigurationError3 extends TimebackError2 {
|
|
59448
|
+
field;
|
|
59449
|
+
constructor(field, message) {
|
|
59450
|
+
super(message || `Missing required configuration: ${field}. Please check your timeback.config.js file.`);
|
|
59451
|
+
this.name = "ConfigurationError";
|
|
59452
|
+
this.field = field;
|
|
59453
|
+
Object.setPrototypeOf(this, ConfigurationError3.prototype);
|
|
59454
|
+
}
|
|
59455
|
+
};
|
|
59456
|
+
ResourceAlreadyExistsError2 = class ResourceAlreadyExistsError3 extends TimebackError2 {
|
|
59457
|
+
resourceType;
|
|
59458
|
+
sourcedId;
|
|
59459
|
+
originalError;
|
|
59460
|
+
constructor(resourceType, sourcedId, originalError) {
|
|
59461
|
+
super(`${resourceType} with ID '${sourcedId}' already exists`);
|
|
59462
|
+
this.name = "ResourceAlreadyExistsError";
|
|
59463
|
+
this.resourceType = resourceType;
|
|
59464
|
+
this.sourcedId = sourcedId;
|
|
59465
|
+
this.originalError = originalError;
|
|
59466
|
+
Object.setPrototypeOf(this, ResourceAlreadyExistsError3.prototype);
|
|
59467
|
+
}
|
|
59468
|
+
};
|
|
59469
|
+
ResourceNotFoundError2 = class ResourceNotFoundError3 extends TimebackError2 {
|
|
59470
|
+
resourceType;
|
|
59471
|
+
sourcedId;
|
|
59472
|
+
constructor(resourceType, sourcedId) {
|
|
59473
|
+
const message = sourcedId === "setup-not-run" ? `TimeBack resources have not been created yet. Please run 'bunx @playcademy/timeback setup' to create the required resources (organization, course, components, etc.) before recording progress.` : `${resourceType} with ID '${sourcedId}' not found`;
|
|
59474
|
+
super(message);
|
|
59475
|
+
this.name = "ResourceNotFoundError";
|
|
59476
|
+
this.resourceType = resourceType;
|
|
59477
|
+
this.sourcedId = sourcedId;
|
|
59478
|
+
Object.setPrototypeOf(this, ResourceNotFoundError3.prototype);
|
|
59479
|
+
}
|
|
59480
|
+
};
|
|
59481
|
+
});
|
|
59482
|
+
init_ids2 = __esm5(() => {
|
|
59483
|
+
init_errors5();
|
|
59484
|
+
UUID_REGEX3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
59485
|
+
});
|
|
58974
59486
|
init_constants8();
|
|
58975
59487
|
init_constants8();
|
|
58976
59488
|
if (process.env.DEBUG === "true") {
|
|
@@ -59130,331 +59642,6 @@ function compareEnrollmentsByRecency(a, b) {
|
|
|
59130
59642
|
var init_timeback_admin_util = __esm(() => {
|
|
59131
59643
|
init_errors();
|
|
59132
59644
|
});
|
|
59133
|
-
function isRecord2(value) {
|
|
59134
|
-
return typeof value === "object" && value !== null;
|
|
59135
|
-
}
|
|
59136
|
-
function filterEnrollmentsByGame(enrollments, gameId) {
|
|
59137
|
-
return enrollments.filter((enrollment) => enrollment.gameId === gameId).map(({ gameId: _2, ...enrollment }) => enrollment);
|
|
59138
|
-
}
|
|
59139
|
-
function mapEnrollmentsToUserEnrollments(enrollments, integrations) {
|
|
59140
|
-
const enrollmentByCourse = new Map(enrollments.map((enrollment) => [enrollment.courseId, enrollment]));
|
|
59141
|
-
const courseToSchool = new Map(enrollments.filter((enrollment) => enrollment.school?.id).map((enrollment) => [enrollment.courseId, enrollment.school.id]));
|
|
59142
|
-
return integrations.map((integration) => {
|
|
59143
|
-
const enrollment = enrollmentByCourse.get(integration.courseId);
|
|
59144
|
-
return {
|
|
59145
|
-
gameId: integration.gameId,
|
|
59146
|
-
grade: integration.grade,
|
|
59147
|
-
subject: integration.subject,
|
|
59148
|
-
courseId: integration.courseId,
|
|
59149
|
-
orgId: courseToSchool.get(integration.courseId),
|
|
59150
|
-
...enrollment ? { id: enrollment.sourcedId } : {}
|
|
59151
|
-
};
|
|
59152
|
-
});
|
|
59153
|
-
}
|
|
59154
|
-
function getStringValue(value) {
|
|
59155
|
-
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
|
59156
|
-
}
|
|
59157
|
-
function parseSourcedIdFromUrl(url2) {
|
|
59158
|
-
if (!url2) {
|
|
59159
|
-
return;
|
|
59160
|
-
}
|
|
59161
|
-
const trimmed = url2.trim().replace(/\/$/, "");
|
|
59162
|
-
if (!trimmed) {
|
|
59163
|
-
return;
|
|
59164
|
-
}
|
|
59165
|
-
const segments = trimmed.split("/");
|
|
59166
|
-
const lastSegment = segments.at(-1);
|
|
59167
|
-
return lastSegment ? decodeURIComponent(lastSegment) : undefined;
|
|
59168
|
-
}
|
|
59169
|
-
function getGeneratedMetricValue(event, type) {
|
|
59170
|
-
const items = event.generated?.items;
|
|
59171
|
-
if (!Array.isArray(items)) {
|
|
59172
|
-
return;
|
|
59173
|
-
}
|
|
59174
|
-
const metric = items.find((item) => item?.type === type);
|
|
59175
|
-
if (!metric) {
|
|
59176
|
-
return;
|
|
59177
|
-
}
|
|
59178
|
-
const value = typeof metric.value === "number" ? metric.value : Number(metric.value);
|
|
59179
|
-
return Number.isFinite(value) ? value : undefined;
|
|
59180
|
-
}
|
|
59181
|
-
function getMergedCaliperExtensions(event) {
|
|
59182
|
-
const objectActivityExtensions = isRecord2(event.object.activity?.extensions) ? event.object.activity.extensions : undefined;
|
|
59183
|
-
const generatedExtensions = isRecord2(event.generated?.extensions) ? event.generated.extensions : undefined;
|
|
59184
|
-
const eventExtensions = isRecord2(event.extensions) ? event.extensions : undefined;
|
|
59185
|
-
return {
|
|
59186
|
-
...objectActivityExtensions,
|
|
59187
|
-
...generatedExtensions,
|
|
59188
|
-
...eventExtensions
|
|
59189
|
-
};
|
|
59190
|
-
}
|
|
59191
|
-
function getPlaycademyMetadata(event) {
|
|
59192
|
-
const extensions = getMergedCaliperExtensions(event);
|
|
59193
|
-
return isRecord2(extensions.playcademy) ? extensions.playcademy : undefined;
|
|
59194
|
-
}
|
|
59195
|
-
function getActivityId(event, playcademy) {
|
|
59196
|
-
const metadataActivityId = getStringValue(playcademy?.activityId);
|
|
59197
|
-
if (metadataActivityId) {
|
|
59198
|
-
return metadataActivityId;
|
|
59199
|
-
}
|
|
59200
|
-
const activityId = getStringValue(event.object.activity?.id);
|
|
59201
|
-
if (activityId) {
|
|
59202
|
-
return activityId;
|
|
59203
|
-
}
|
|
59204
|
-
const objectId = getStringValue(event.object.id);
|
|
59205
|
-
if (!objectId) {
|
|
59206
|
-
return;
|
|
59207
|
-
}
|
|
59208
|
-
const trimmed = objectId.replace(/\/$/, "");
|
|
59209
|
-
const segments = trimmed.split("/");
|
|
59210
|
-
const activityIndex = segments.lastIndexOf("activities");
|
|
59211
|
-
if (activityIndex !== -1 && segments.length >= activityIndex + 3) {
|
|
59212
|
-
const candidate = segments[activityIndex + 2];
|
|
59213
|
-
return candidate ? decodeURIComponent(candidate) : undefined;
|
|
59214
|
-
}
|
|
59215
|
-
return;
|
|
59216
|
-
}
|
|
59217
|
-
function buildResourceMetadata({
|
|
59218
|
-
baseMetadata,
|
|
59219
|
-
subject,
|
|
59220
|
-
grade,
|
|
59221
|
-
totalXp,
|
|
59222
|
-
masterableUnits
|
|
59223
|
-
}) {
|
|
59224
|
-
const normalizedBaseMetadata = isResourceMetadata(baseMetadata) ? baseMetadata : undefined;
|
|
59225
|
-
const metadata2 = {
|
|
59226
|
-
...normalizedBaseMetadata
|
|
59227
|
-
};
|
|
59228
|
-
metadata2.subject = subject;
|
|
59229
|
-
metadata2.grades = [grade];
|
|
59230
|
-
metadata2.xp = totalXp;
|
|
59231
|
-
if (masterableUnits !== undefined && masterableUnits !== null) {
|
|
59232
|
-
const existingPlaycademy = isPlaycademyResourceMetadata2(metadata2.playcademy) ? metadata2.playcademy : undefined;
|
|
59233
|
-
metadata2.playcademy = {
|
|
59234
|
-
...existingPlaycademy,
|
|
59235
|
-
mastery: {
|
|
59236
|
-
...existingPlaycademy?.mastery,
|
|
59237
|
-
masterableUnits
|
|
59238
|
-
}
|
|
59239
|
-
};
|
|
59240
|
-
}
|
|
59241
|
-
return metadata2;
|
|
59242
|
-
}
|
|
59243
|
-
function getDurationSecondsFromExtensions(event) {
|
|
59244
|
-
const extensions = getMergedCaliperExtensions(event);
|
|
59245
|
-
const playcademy = isRecord2(extensions.playcademy) ? extensions.playcademy : undefined;
|
|
59246
|
-
const rawValue = extensions.durationSeconds ?? playcademy?.durationSeconds;
|
|
59247
|
-
const value = typeof rawValue === "number" ? rawValue : Number(rawValue);
|
|
59248
|
-
return Number.isFinite(value) ? value : undefined;
|
|
59249
|
-
}
|
|
59250
|
-
function getCanonicalRunId(session2) {
|
|
59251
|
-
const sessionId = getStringValue(session2?.id);
|
|
59252
|
-
if (!sessionId) {
|
|
59253
|
-
return;
|
|
59254
|
-
}
|
|
59255
|
-
return sessionId.replace(/^urn:uuid:/, "");
|
|
59256
|
-
}
|
|
59257
|
-
function getResumeId(event) {
|
|
59258
|
-
const playcademy = getPlaycademyMetadata(event);
|
|
59259
|
-
return getStringValue(playcademy?.resumeId);
|
|
59260
|
-
}
|
|
59261
|
-
function isCaliperRemediationOrCompletionEvent(event) {
|
|
59262
|
-
const playcademy = getPlaycademyMetadata(event);
|
|
59263
|
-
return REMEDIATION_OR_COMPLETION_EVENT_KINDS.has(getStringValue(playcademy?.eventKind) || "");
|
|
59264
|
-
}
|
|
59265
|
-
function groupCaliperEventsByRun(events) {
|
|
59266
|
-
const groups = new Map;
|
|
59267
|
-
for (const event of events) {
|
|
59268
|
-
const objectId = getStringValue(event.object.id) || "unknown-activity";
|
|
59269
|
-
const groupKey = `${objectId}::${getStringValue(event.session?.id) || event.externalId}`;
|
|
59270
|
-
const existing = groups.get(groupKey);
|
|
59271
|
-
if (existing) {
|
|
59272
|
-
existing.push(event);
|
|
59273
|
-
} else {
|
|
59274
|
-
groups.set(groupKey, [event]);
|
|
59275
|
-
}
|
|
59276
|
-
}
|
|
59277
|
-
return groups;
|
|
59278
|
-
}
|
|
59279
|
-
function findCaliperEventGroupContainingExternalId(events, externalId) {
|
|
59280
|
-
const targetExternalId = externalId.trim();
|
|
59281
|
-
if (!targetExternalId) {
|
|
59282
|
-
return;
|
|
59283
|
-
}
|
|
59284
|
-
return [...groupCaliperEventsByRun(events).values()].find((group) => group.some((event) => event.externalId === targetExternalId));
|
|
59285
|
-
}
|
|
59286
|
-
function mapCaliperEventGroupToActivity(events, relevantCourseIds) {
|
|
59287
|
-
if (events.length === 0) {
|
|
59288
|
-
return null;
|
|
59289
|
-
}
|
|
59290
|
-
const sortedEvents = events.toSorted((a, b) => a.eventTime.localeCompare(b.eventTime));
|
|
59291
|
-
const activityEvent = [...sortedEvents].toReversed().find((event) => event.type === "ActivityEvent");
|
|
59292
|
-
const contextSource = activityEvent || sortedEvents.at(-1);
|
|
59293
|
-
if (!contextSource) {
|
|
59294
|
-
return null;
|
|
59295
|
-
}
|
|
59296
|
-
const ctx = parseCaliperEventContext(contextSource, relevantCourseIds);
|
|
59297
|
-
if (!ctx) {
|
|
59298
|
-
return null;
|
|
59299
|
-
}
|
|
59300
|
-
const score = activityEvent !== undefined ? (() => {
|
|
59301
|
-
const totalQuestions = getGeneratedMetricValue(activityEvent, "totalQuestions");
|
|
59302
|
-
const correctQuestions = getGeneratedMetricValue(activityEvent, "correctQuestions");
|
|
59303
|
-
if (totalQuestions === undefined || correctQuestions === undefined || totalQuestions <= 0) {
|
|
59304
|
-
return;
|
|
59305
|
-
}
|
|
59306
|
-
return correctQuestions / totalQuestions * 100;
|
|
59307
|
-
})() : undefined;
|
|
59308
|
-
const xpEarned = activityEvent !== undefined ? getGeneratedMetricValue(activityEvent, "xpEarned") : undefined;
|
|
59309
|
-
const masteredUnits = activityEvent !== undefined ? getGeneratedMetricValue(activityEvent, "masteredUnits") : undefined;
|
|
59310
|
-
const timeSpentEvents = sortedEvents.filter((event) => event.type === "TimeSpentEvent");
|
|
59311
|
-
let totalActiveTimeSeconds;
|
|
59312
|
-
if (timeSpentEvents.length > 0) {
|
|
59313
|
-
totalActiveTimeSeconds = timeSpentEvents.reduce((sum, event) => sum + (getGeneratedMetricValue(event, "active") ?? 0), 0);
|
|
59314
|
-
} else if (activityEvent !== undefined) {
|
|
59315
|
-
totalActiveTimeSeconds = getDurationSecondsFromExtensions(activityEvent);
|
|
59316
|
-
}
|
|
59317
|
-
const fallbackActivityId = getActivityId(contextSource, getPlaycademyMetadata(contextSource));
|
|
59318
|
-
const occurredAt = getStringValue(activityEvent?.eventTime) || getStringValue(sortedEvents.at(-1)?.eventTime);
|
|
59319
|
-
const runId = getCanonicalRunId(contextSource.session);
|
|
59320
|
-
const resumeIds = new Set(sortedEvents.map((event) => getResumeId(event)).filter((resumeId) => resumeId !== undefined));
|
|
59321
|
-
const sessionCount = resumeIds.size > 0 ? resumeIds.size : 1;
|
|
59322
|
-
const kind = activityEvent !== undefined ? "activity" : "activity-in-progress";
|
|
59323
|
-
if (!occurredAt) {
|
|
59324
|
-
return null;
|
|
59325
|
-
}
|
|
59326
|
-
return {
|
|
59327
|
-
id: activityEvent?.externalId || sortedEvents.at(-1)?.externalId || events[0].externalId,
|
|
59328
|
-
kind,
|
|
59329
|
-
occurredAt,
|
|
59330
|
-
courseId: ctx.courseId,
|
|
59331
|
-
title: getStringValue(activityEvent?.object.activity?.name) || ctx.titleFromEvent || (fallbackActivityId ? kebabToTitleCase(fallbackActivityId) : "Activity completed"),
|
|
59332
|
-
...ctx.activityId ? { activityId: ctx.activityId } : {},
|
|
59333
|
-
...ctx.appName ? { appName: ctx.appName } : {},
|
|
59334
|
-
...score !== undefined ? { score } : {},
|
|
59335
|
-
...xpEarned !== undefined ? { xpDelta: xpEarned } : {},
|
|
59336
|
-
...masteredUnits !== undefined ? { masteredUnitsDelta: masteredUnits } : {},
|
|
59337
|
-
...totalActiveTimeSeconds !== undefined ? { timeDeltaSeconds: totalActiveTimeSeconds } : {},
|
|
59338
|
-
...runId ? { runId } : {},
|
|
59339
|
-
...sessionCount > 0 ? { sessionCount } : {}
|
|
59340
|
-
};
|
|
59341
|
-
}
|
|
59342
|
-
function parseCaliperEventContext(event, relevantCourseIds) {
|
|
59343
|
-
const playcademy = getPlaycademyMetadata(event);
|
|
59344
|
-
const courseId = getStringValue(playcademy?.courseId) || parseSourcedIdFromUrl(event.object.course?.id);
|
|
59345
|
-
if (!courseId || !relevantCourseIds.has(courseId)) {
|
|
59346
|
-
return null;
|
|
59347
|
-
}
|
|
59348
|
-
const occurredAt = getStringValue(event.eventTime);
|
|
59349
|
-
if (!occurredAt) {
|
|
59350
|
-
return null;
|
|
59351
|
-
}
|
|
59352
|
-
return {
|
|
59353
|
-
courseId,
|
|
59354
|
-
occurredAt,
|
|
59355
|
-
eventKind: getStringValue(playcademy?.eventKind),
|
|
59356
|
-
source: getStringValue(playcademy?.source),
|
|
59357
|
-
reason: getStringValue(playcademy?.reason),
|
|
59358
|
-
titleFromEvent: getStringValue(event.object.activity?.name),
|
|
59359
|
-
appName: getStringValue(event.object.app?.name),
|
|
59360
|
-
activityId: getActivityId(event, playcademy)
|
|
59361
|
-
};
|
|
59362
|
-
}
|
|
59363
|
-
function mapTimeSpentRemediation(event, ctx) {
|
|
59364
|
-
if (ctx.eventKind !== "remediation-time") {
|
|
59365
|
-
return null;
|
|
59366
|
-
}
|
|
59367
|
-
return {
|
|
59368
|
-
id: event.externalId,
|
|
59369
|
-
kind: "remediation-time",
|
|
59370
|
-
occurredAt: ctx.occurredAt,
|
|
59371
|
-
courseId: ctx.courseId,
|
|
59372
|
-
title: "Time Adjustment",
|
|
59373
|
-
activityId: ctx.activityId,
|
|
59374
|
-
appName: ctx.appName,
|
|
59375
|
-
reason: ctx.reason,
|
|
59376
|
-
timeDeltaSeconds: getGeneratedMetricValue(event, "active")
|
|
59377
|
-
};
|
|
59378
|
-
}
|
|
59379
|
-
function mapActivityRemediation(event, ctx) {
|
|
59380
|
-
if (ctx.eventKind === "remediation-xp") {
|
|
59381
|
-
return {
|
|
59382
|
-
id: event.externalId,
|
|
59383
|
-
kind: "remediation-xp",
|
|
59384
|
-
occurredAt: ctx.occurredAt,
|
|
59385
|
-
courseId: ctx.courseId,
|
|
59386
|
-
title: "XP Adjustment",
|
|
59387
|
-
activityId: ctx.activityId,
|
|
59388
|
-
appName: ctx.appName,
|
|
59389
|
-
reason: ctx.reason,
|
|
59390
|
-
xpDelta: getGeneratedMetricValue(event, "xpEarned"),
|
|
59391
|
-
masteredUnitsDelta: getGeneratedMetricValue(event, "masteredUnits")
|
|
59392
|
-
};
|
|
59393
|
-
}
|
|
59394
|
-
if (ctx.eventKind === "remediation-mastery") {
|
|
59395
|
-
return {
|
|
59396
|
-
id: event.externalId,
|
|
59397
|
-
kind: "remediation-mastery",
|
|
59398
|
-
occurredAt: ctx.occurredAt,
|
|
59399
|
-
courseId: ctx.courseId,
|
|
59400
|
-
title: "Mastery Adjustment",
|
|
59401
|
-
activityId: ctx.activityId,
|
|
59402
|
-
appName: ctx.appName,
|
|
59403
|
-
reason: ctx.reason,
|
|
59404
|
-
xpDelta: getGeneratedMetricValue(event, "xpEarned"),
|
|
59405
|
-
masteredUnitsDelta: getGeneratedMetricValue(event, "masteredUnits")
|
|
59406
|
-
};
|
|
59407
|
-
}
|
|
59408
|
-
if (ctx.eventKind === "course-completed") {
|
|
59409
|
-
return {
|
|
59410
|
-
id: event.externalId,
|
|
59411
|
-
kind: "course-completed",
|
|
59412
|
-
occurredAt: ctx.occurredAt,
|
|
59413
|
-
courseId: ctx.courseId,
|
|
59414
|
-
title: ctx.source === "admin" ? "Course marked complete" : "Course completed",
|
|
59415
|
-
activityId: ctx.activityId,
|
|
59416
|
-
appName: ctx.appName,
|
|
59417
|
-
reason: ctx.reason
|
|
59418
|
-
};
|
|
59419
|
-
}
|
|
59420
|
-
if (ctx.eventKind === "course-resumed") {
|
|
59421
|
-
return {
|
|
59422
|
-
id: event.externalId,
|
|
59423
|
-
kind: "course-resumed",
|
|
59424
|
-
occurredAt: ctx.occurredAt,
|
|
59425
|
-
courseId: ctx.courseId,
|
|
59426
|
-
title: "Course resumed",
|
|
59427
|
-
activityId: ctx.activityId,
|
|
59428
|
-
appName: ctx.appName,
|
|
59429
|
-
reason: ctx.reason
|
|
59430
|
-
};
|
|
59431
|
-
}
|
|
59432
|
-
return null;
|
|
59433
|
-
}
|
|
59434
|
-
function mapCaliperEventToRemediationActivity(event, relevantCourseIds) {
|
|
59435
|
-
const ctx = parseCaliperEventContext(event, relevantCourseIds);
|
|
59436
|
-
if (!ctx) {
|
|
59437
|
-
return null;
|
|
59438
|
-
}
|
|
59439
|
-
if (event.type === "TimeSpentEvent") {
|
|
59440
|
-
return mapTimeSpentRemediation(event, ctx);
|
|
59441
|
-
}
|
|
59442
|
-
if (event.type === "ActivityEvent") {
|
|
59443
|
-
return mapActivityRemediation(event, ctx);
|
|
59444
|
-
}
|
|
59445
|
-
return null;
|
|
59446
|
-
}
|
|
59447
|
-
var REMEDIATION_OR_COMPLETION_EVENT_KINDS;
|
|
59448
|
-
var init_timeback_util = __esm(() => {
|
|
59449
|
-
init_types3();
|
|
59450
|
-
REMEDIATION_OR_COMPLETION_EVENT_KINDS = new Set([
|
|
59451
|
-
"remediation-xp",
|
|
59452
|
-
"remediation-time",
|
|
59453
|
-
"remediation-mastery",
|
|
59454
|
-
"course-completed",
|
|
59455
|
-
"course-resumed"
|
|
59456
|
-
]);
|
|
59457
|
-
});
|
|
59458
59645
|
function parseDateInputParts(value) {
|
|
59459
59646
|
const parts2 = value.split("-");
|
|
59460
59647
|
return {
|
|
@@ -59823,7 +60010,7 @@ function isAllowedGradeLevelTestType(value) {
|
|
|
59823
60010
|
}
|
|
59824
60011
|
function isQualifyingGradeLevelTestResult(result, options) {
|
|
59825
60012
|
const metadata2 = metadataOf(result);
|
|
59826
|
-
return sourceIdFromRef(result.student) === options.studentId && normalizeText(result.status) === "active" && result.scoreStatus ===
|
|
60013
|
+
return sourceIdFromRef(result.student) === options.studentId && normalizeText(result.status) === "active" && result.scoreStatus === SCORE_STATUS3.fullyGraded && normalizeText(metadata2.resultType) === "assessment" && stringField(metadata2.subject).trim() === options.subject && isAllowedGradeLevelTestType(metadata2.testType);
|
|
59827
60014
|
}
|
|
59828
60015
|
function mapGradeLevelTestSummary(result, options) {
|
|
59829
60016
|
if (!isQualifyingGradeLevelTestResult(result, options)) {
|
|
@@ -60083,7 +60270,7 @@ function latestAssessmentResultsByLineItem(results) {
|
|
|
60083
60270
|
const latestResultsByLineItem = new Map;
|
|
60084
60271
|
for (const result of results) {
|
|
60085
60272
|
const lineItemId = sourceIdFromRef(result.assessmentLineItem);
|
|
60086
|
-
if (lineItemId && !latestResultsByLineItem.has(lineItemId) && result.scoreStatus ===
|
|
60273
|
+
if (lineItemId && !latestResultsByLineItem.has(lineItemId) && result.scoreStatus === SCORE_STATUS3.fullyGraded) {
|
|
60087
60274
|
latestResultsByLineItem.set(lineItemId, result);
|
|
60088
60275
|
}
|
|
60089
60276
|
}
|
|
@@ -60184,7 +60371,7 @@ function qtiIdCandidates(parentLineItem, resource) {
|
|
|
60184
60371
|
var GRADE_LEVEL_TEST_TYPES;
|
|
60185
60372
|
var naturalTitleSort;
|
|
60186
60373
|
var init_timeback_grade_level_results_util = __esm(() => {
|
|
60187
|
-
|
|
60374
|
+
init_constants6();
|
|
60188
60375
|
init_utils6();
|
|
60189
60376
|
GRADE_LEVEL_TEST_TYPES = [
|
|
60190
60377
|
"placement",
|
|
@@ -60207,18 +60394,18 @@ async function upsertMasteryCompletionEntry(params) {
|
|
|
60207
60394
|
await client.oneroster.assessmentLineItems.findOrCreate(lineItemId, {
|
|
60208
60395
|
sourcedId: lineItemId,
|
|
60209
60396
|
title: "Mastery Completion",
|
|
60210
|
-
status:
|
|
60397
|
+
status: ONEROSTER_STATUS3.active,
|
|
60211
60398
|
course: { sourcedId: ids.course },
|
|
60212
60399
|
...ids.componentResource ? { componentResource: { sourcedId: ids.componentResource } } : {}
|
|
60213
60400
|
});
|
|
60214
60401
|
await client.oneroster.assessmentResults.upsert(resultId, {
|
|
60215
60402
|
sourcedId: resultId,
|
|
60216
|
-
status:
|
|
60403
|
+
status: ONEROSTER_STATUS3.active,
|
|
60217
60404
|
assessmentLineItem: { sourcedId: lineItemId },
|
|
60218
60405
|
student: { sourcedId: studentId },
|
|
60219
60406
|
score: 100,
|
|
60220
60407
|
scoreDate: new Date().toISOString(),
|
|
60221
|
-
scoreStatus:
|
|
60408
|
+
scoreStatus: SCORE_STATUS3.fullyGraded,
|
|
60222
60409
|
inProgress: "false",
|
|
60223
60410
|
metadata: {
|
|
60224
60411
|
isMasteryCompletion: true,
|
|
@@ -60230,12 +60417,12 @@ async function upsertMasteryCompletionEntry(params) {
|
|
|
60230
60417
|
try {
|
|
60231
60418
|
await client.oneroster.assessmentResults.upsert(resultId, {
|
|
60232
60419
|
sourcedId: resultId,
|
|
60233
|
-
status:
|
|
60420
|
+
status: ONEROSTER_STATUS3.active,
|
|
60234
60421
|
assessmentLineItem: { sourcedId: lineItemId },
|
|
60235
60422
|
student: { sourcedId: studentId },
|
|
60236
60423
|
score: 0,
|
|
60237
60424
|
scoreDate: new Date().toISOString(),
|
|
60238
|
-
scoreStatus:
|
|
60425
|
+
scoreStatus: SCORE_STATUS3.notSubmitted,
|
|
60239
60426
|
inProgress: "true",
|
|
60240
60427
|
metadata: {
|
|
60241
60428
|
isMasteryCompletion: true,
|
|
@@ -60247,7 +60434,7 @@ async function upsertMasteryCompletionEntry(params) {
|
|
|
60247
60434
|
}
|
|
60248
60435
|
}
|
|
60249
60436
|
var init_timeback_mastery_completion_util = __esm(() => {
|
|
60250
|
-
|
|
60437
|
+
init_constants6();
|
|
60251
60438
|
init_utils6();
|
|
60252
60439
|
});
|
|
60253
60440
|
|
|
@@ -60275,7 +60462,7 @@ class TimebackAdminService {
|
|
|
60275
60462
|
this.deps = deps;
|
|
60276
60463
|
}
|
|
60277
60464
|
getGradeLevelTestCourseScope(integration) {
|
|
60278
|
-
if (!
|
|
60465
|
+
if (!isTimebackSubject2(integration.subject) || !isTimebackGrade2(integration.grade)) {
|
|
60279
60466
|
throw new ValidationError("Timeback integration has invalid grade or subject");
|
|
60280
60467
|
}
|
|
60281
60468
|
return {
|
|
@@ -60354,7 +60541,7 @@ class TimebackAdminService {
|
|
|
60354
60541
|
await this.deps.validateDeveloperAccess(user, gameId);
|
|
60355
60542
|
}
|
|
60356
60543
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
60357
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
60544
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus())
|
|
60358
60545
|
});
|
|
60359
60546
|
if (!integration) {
|
|
60360
60547
|
throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
|
|
@@ -60452,7 +60639,7 @@ class TimebackAdminService {
|
|
|
60452
60639
|
const ids = deriveSourcedIds2(courseId);
|
|
60453
60640
|
const resource = await client.oneroster.resources.get(ids.resource);
|
|
60454
60641
|
const playcademyMetadata = resource.metadata?.playcademy;
|
|
60455
|
-
if (!
|
|
60642
|
+
if (!isPlaycademyResourceMetadata(playcademyMetadata)) {
|
|
60456
60643
|
return;
|
|
60457
60644
|
}
|
|
60458
60645
|
return playcademyMetadata?.mastery?.masterableUnits;
|
|
@@ -60856,7 +61043,7 @@ class TimebackAdminService {
|
|
|
60856
61043
|
"app.timeback.include_inactive": options?.includeInactive ?? false
|
|
60857
61044
|
});
|
|
60858
61045
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
60859
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
61046
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus())
|
|
60860
61047
|
});
|
|
60861
61048
|
if (!integration) {
|
|
60862
61049
|
throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
|
|
@@ -60934,7 +61121,7 @@ class TimebackAdminService {
|
|
|
60934
61121
|
columns: { id: true }
|
|
60935
61122
|
}),
|
|
60936
61123
|
this.deps.db.query.gameTimebackIntegrations.findMany({
|
|
60937
|
-
where: eq(gameTimebackIntegrations.gameId, gameId)
|
|
61124
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), isActiveGameTimebackIntegrationStatus())
|
|
60938
61125
|
}),
|
|
60939
61126
|
this.deps.db.query.games.findFirst({
|
|
60940
61127
|
where: eq(games.id, gameId),
|
|
@@ -61037,7 +61224,7 @@ class TimebackAdminService {
|
|
|
61037
61224
|
"app.timeback.course_id": courseId
|
|
61038
61225
|
});
|
|
61039
61226
|
const integrations = await this.deps.db.query.gameTimebackIntegrations.findMany({
|
|
61040
|
-
where: courseId ? and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId)) : eq(gameTimebackIntegrations.gameId, gameId)
|
|
61227
|
+
where: courseId ? and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus()) : and(eq(gameTimebackIntegrations.gameId, gameId), isActiveGameTimebackIntegrationStatus())
|
|
61041
61228
|
});
|
|
61042
61229
|
if (integrations.length === 0) {
|
|
61043
61230
|
throw new NotFoundError("Timeback integration", gameId);
|
|
@@ -61127,7 +61314,7 @@ class TimebackAdminService {
|
|
|
61127
61314
|
});
|
|
61128
61315
|
const [integration, gameSource] = await Promise.all([
|
|
61129
61316
|
this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
61130
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
61317
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus())
|
|
61131
61318
|
}),
|
|
61132
61319
|
this.getGameActivitySource(gameId)
|
|
61133
61320
|
]);
|
|
@@ -61231,7 +61418,7 @@ class TimebackAdminService {
|
|
|
61231
61418
|
"app.timeback.course_id": courseId
|
|
61232
61419
|
});
|
|
61233
61420
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
61234
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
61421
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus())
|
|
61235
61422
|
});
|
|
61236
61423
|
if (!integration) {
|
|
61237
61424
|
throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
|
|
@@ -61286,7 +61473,7 @@ class TimebackAdminService {
|
|
|
61286
61473
|
"app.timeback.assessment_result_id": resultId
|
|
61287
61474
|
});
|
|
61288
61475
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
61289
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
61476
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus())
|
|
61290
61477
|
});
|
|
61291
61478
|
if (!integration) {
|
|
61292
61479
|
throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
|
|
@@ -61364,7 +61551,7 @@ class TimebackAdminService {
|
|
|
61364
61551
|
});
|
|
61365
61552
|
const [integration, gameSource, roster] = await Promise.all([
|
|
61366
61553
|
this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
61367
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
61554
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus())
|
|
61368
61555
|
}),
|
|
61369
61556
|
this.getGameActivitySource(gameId),
|
|
61370
61557
|
client.oneroster.enrollments.listByCourse(courseId, {
|
|
@@ -61485,7 +61672,7 @@ class TimebackAdminService {
|
|
|
61485
61672
|
});
|
|
61486
61673
|
const [integration, gameSource] = await Promise.all([
|
|
61487
61674
|
this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
61488
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
61675
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus())
|
|
61489
61676
|
}),
|
|
61490
61677
|
this.getGameActivitySource(gameId)
|
|
61491
61678
|
]);
|
|
@@ -61569,7 +61756,7 @@ class TimebackAdminService {
|
|
|
61569
61756
|
});
|
|
61570
61757
|
const [integration, gameSource] = await Promise.all([
|
|
61571
61758
|
this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
61572
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
61759
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus())
|
|
61573
61760
|
}),
|
|
61574
61761
|
this.getGameActivitySource(gameId)
|
|
61575
61762
|
]);
|
|
@@ -61798,7 +61985,7 @@ class TimebackAdminService {
|
|
|
61798
61985
|
"app.timeback.course_id": courseId
|
|
61799
61986
|
});
|
|
61800
61987
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
61801
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
61988
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus())
|
|
61802
61989
|
});
|
|
61803
61990
|
if (!integration) {
|
|
61804
61991
|
throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
|
|
@@ -61869,7 +62056,7 @@ class TimebackAdminService {
|
|
|
61869
62056
|
"app.timeback.enrollment.operation": "enroll"
|
|
61870
62057
|
});
|
|
61871
62058
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
61872
|
-
where: and(eq(gameTimebackIntegrations.gameId, data.gameId), eq(gameTimebackIntegrations.courseId, data.courseId))
|
|
62059
|
+
where: and(eq(gameTimebackIntegrations.gameId, data.gameId), eq(gameTimebackIntegrations.courseId, data.courseId), isActiveGameTimebackIntegrationStatus())
|
|
61873
62060
|
});
|
|
61874
62061
|
if (!integration) {
|
|
61875
62062
|
throw new NotFoundError("Timeback integration", `${data.gameId}:${data.courseId}`);
|
|
@@ -61909,7 +62096,7 @@ class TimebackAdminService {
|
|
|
61909
62096
|
"app.timeback.enrollment.operation": "unenroll"
|
|
61910
62097
|
});
|
|
61911
62098
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
61912
|
-
where: and(eq(gameTimebackIntegrations.gameId, data.gameId), eq(gameTimebackIntegrations.courseId, data.courseId))
|
|
62099
|
+
where: and(eq(gameTimebackIntegrations.gameId, data.gameId), eq(gameTimebackIntegrations.courseId, data.courseId), isActiveGameTimebackIntegrationStatus())
|
|
61913
62100
|
});
|
|
61914
62101
|
if (!integration) {
|
|
61915
62102
|
throw new NotFoundError("Timeback integration", `${data.gameId}:${data.courseId}`);
|
|
@@ -61930,7 +62117,7 @@ class TimebackAdminService {
|
|
|
61930
62117
|
"app.timeback.enrollment.operation": "reactivate"
|
|
61931
62118
|
});
|
|
61932
62119
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
61933
|
-
where: and(eq(gameTimebackIntegrations.gameId, data.gameId), eq(gameTimebackIntegrations.courseId, data.courseId))
|
|
62120
|
+
where: and(eq(gameTimebackIntegrations.gameId, data.gameId), eq(gameTimebackIntegrations.courseId, data.courseId), isActiveGameTimebackIntegrationStatus())
|
|
61934
62121
|
});
|
|
61935
62122
|
if (!integration) {
|
|
61936
62123
|
throw new NotFoundError("Timeback integration", `${data.gameId}:${data.courseId}`);
|
|
@@ -61976,7 +62163,7 @@ class TimebackAdminService {
|
|
|
61976
62163
|
const resultId = `${lineItemId}:${studentId}:completion`;
|
|
61977
62164
|
try {
|
|
61978
62165
|
const result = await client.oneroster.assessmentResults.get(resultId);
|
|
61979
|
-
if (result.scoreStatus ===
|
|
62166
|
+
if (result.scoreStatus === SCORE_STATUS3.fullyGraded) {
|
|
61980
62167
|
return "complete";
|
|
61981
62168
|
}
|
|
61982
62169
|
return "incomplete";
|
|
@@ -62012,12 +62199,13 @@ class TimebackAdminService {
|
|
|
62012
62199
|
var init_timeback_admin_service = __esm(() => {
|
|
62013
62200
|
init_drizzle_orm();
|
|
62014
62201
|
init_src();
|
|
62202
|
+
init_helpers_index();
|
|
62015
62203
|
init_schemas_index();
|
|
62016
62204
|
init_tables_index();
|
|
62017
62205
|
init_spans();
|
|
62018
62206
|
init_dist2();
|
|
62019
|
-
|
|
62020
|
-
|
|
62207
|
+
init_constants6();
|
|
62208
|
+
init_types2();
|
|
62021
62209
|
init_utils6();
|
|
62022
62210
|
init_src4();
|
|
62023
62211
|
init_timeback3();
|
|
@@ -62059,8 +62247,15 @@ var RESOURCE_DEFAULTS5;
|
|
|
62059
62247
|
var HTTP_STATUS5;
|
|
62060
62248
|
var ERROR_NAMES5;
|
|
62061
62249
|
var init_constants9;
|
|
62062
|
-
var
|
|
62063
|
-
var
|
|
62250
|
+
var TimebackError3;
|
|
62251
|
+
var TimebackApiError3;
|
|
62252
|
+
var TimebackAuthenticationError3;
|
|
62253
|
+
var StudentNotFoundError3;
|
|
62254
|
+
var ConfigurationError3;
|
|
62255
|
+
var ResourceAlreadyExistsError3;
|
|
62256
|
+
var ResourceNotFoundError3;
|
|
62257
|
+
var init_errors7;
|
|
62258
|
+
var init_errors6 = __esm(() => {
|
|
62064
62259
|
init_src();
|
|
62065
62260
|
init_constants9 = __esm6(() => {
|
|
62066
62261
|
TIMEBACK_API_URLS5 = {
|
|
@@ -62244,18 +62439,79 @@ var init_errors4 = __esm(() => {
|
|
|
62244
62439
|
timebackSdk: "TimebackSDKError"
|
|
62245
62440
|
};
|
|
62246
62441
|
});
|
|
62247
|
-
|
|
62248
|
-
|
|
62249
|
-
|
|
62250
|
-
|
|
62251
|
-
|
|
62252
|
-
|
|
62253
|
-
|
|
62254
|
-
|
|
62255
|
-
|
|
62256
|
-
|
|
62257
|
-
|
|
62258
|
-
|
|
62442
|
+
init_errors7 = __esm6(() => {
|
|
62443
|
+
init_constants9();
|
|
62444
|
+
TimebackError3 = class TimebackError4 extends Error {
|
|
62445
|
+
constructor(message) {
|
|
62446
|
+
super(message);
|
|
62447
|
+
this.name = ERROR_NAMES5.timebackSdk;
|
|
62448
|
+
}
|
|
62449
|
+
};
|
|
62450
|
+
TimebackApiError3 = class TimebackApiError4 extends Error {
|
|
62451
|
+
status;
|
|
62452
|
+
details;
|
|
62453
|
+
constructor(status, message, details) {
|
|
62454
|
+
super(`${status} ${message}`);
|
|
62455
|
+
this.name = ERROR_NAMES5.timebackApi;
|
|
62456
|
+
this.status = status;
|
|
62457
|
+
this.details = details;
|
|
62458
|
+
Object.setPrototypeOf(this, TimebackApiError4.prototype);
|
|
62459
|
+
}
|
|
62460
|
+
};
|
|
62461
|
+
TimebackAuthenticationError3 = class TimebackAuthenticationError4 extends TimebackError3 {
|
|
62462
|
+
constructor(message) {
|
|
62463
|
+
super(message || "Authentication failed. Please verify TIMEBACK_CLIENT_ID and TIMEBACK_CLIENT_SECRET are set correctly.");
|
|
62464
|
+
this.name = "TimebackAuthenticationError";
|
|
62465
|
+
Object.setPrototypeOf(this, TimebackAuthenticationError4.prototype);
|
|
62466
|
+
}
|
|
62467
|
+
};
|
|
62468
|
+
StudentNotFoundError3 = class StudentNotFoundError4 extends TimebackError3 {
|
|
62469
|
+
identifier;
|
|
62470
|
+
identifierType;
|
|
62471
|
+
constructor(identifier, identifierType = "email") {
|
|
62472
|
+
super(`Student not found with ${identifierType}: ${identifier}. Ensure the student exists in OneRoster. If this is a new student, they must be created in OneRoster first.`);
|
|
62473
|
+
this.name = "StudentNotFoundError";
|
|
62474
|
+
this.identifier = identifier;
|
|
62475
|
+
this.identifierType = identifierType;
|
|
62476
|
+
Object.setPrototypeOf(this, StudentNotFoundError4.prototype);
|
|
62477
|
+
}
|
|
62478
|
+
};
|
|
62479
|
+
ConfigurationError3 = class ConfigurationError4 extends TimebackError3 {
|
|
62480
|
+
field;
|
|
62481
|
+
constructor(field, message) {
|
|
62482
|
+
super(message || `Missing required configuration: ${field}. Please check your timeback.config.js file.`);
|
|
62483
|
+
this.name = "ConfigurationError";
|
|
62484
|
+
this.field = field;
|
|
62485
|
+
Object.setPrototypeOf(this, ConfigurationError4.prototype);
|
|
62486
|
+
}
|
|
62487
|
+
};
|
|
62488
|
+
ResourceAlreadyExistsError3 = class ResourceAlreadyExistsError4 extends TimebackError3 {
|
|
62489
|
+
resourceType;
|
|
62490
|
+
sourcedId;
|
|
62491
|
+
originalError;
|
|
62492
|
+
constructor(resourceType, sourcedId, originalError) {
|
|
62493
|
+
super(`${resourceType} with ID '${sourcedId}' already exists`);
|
|
62494
|
+
this.name = "ResourceAlreadyExistsError";
|
|
62495
|
+
this.resourceType = resourceType;
|
|
62496
|
+
this.sourcedId = sourcedId;
|
|
62497
|
+
this.originalError = originalError;
|
|
62498
|
+
Object.setPrototypeOf(this, ResourceAlreadyExistsError4.prototype);
|
|
62499
|
+
}
|
|
62500
|
+
};
|
|
62501
|
+
ResourceNotFoundError3 = class ResourceNotFoundError4 extends TimebackError3 {
|
|
62502
|
+
resourceType;
|
|
62503
|
+
sourcedId;
|
|
62504
|
+
constructor(resourceType, sourcedId) {
|
|
62505
|
+
const message = sourcedId === "setup-not-run" ? `TimeBack resources have not been created yet. Please run 'bunx @playcademy/timeback setup' to create the required resources (organization, course, components, etc.) before recording progress.` : `${resourceType} with ID '${sourcedId}' not found`;
|
|
62506
|
+
super(message);
|
|
62507
|
+
this.name = "ResourceNotFoundError";
|
|
62508
|
+
this.resourceType = resourceType;
|
|
62509
|
+
this.sourcedId = sourcedId;
|
|
62510
|
+
Object.setPrototypeOf(this, ResourceNotFoundError4.prototype);
|
|
62511
|
+
}
|
|
62512
|
+
};
|
|
62513
|
+
});
|
|
62514
|
+
init_errors7();
|
|
62259
62515
|
});
|
|
62260
62516
|
|
|
62261
62517
|
class TimebackAssessmentsService {
|
|
@@ -62266,7 +62522,7 @@ class TimebackAssessmentsService {
|
|
|
62266
62522
|
async resolveIntegrationId(gameId, courseId, user) {
|
|
62267
62523
|
await this.deps.validateGameManagementAccess(user, gameId);
|
|
62268
62524
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
62269
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
62525
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus())
|
|
62270
62526
|
});
|
|
62271
62527
|
if (!integration) {
|
|
62272
62528
|
throw new NotFoundError(`No Timeback integration found for game ${gameId} course ${courseId}`);
|
|
@@ -62359,7 +62615,7 @@ class TimebackAssessmentsService {
|
|
|
62359
62615
|
]
|
|
62360
62616
|
});
|
|
62361
62617
|
} catch (error) {
|
|
62362
|
-
if (error instanceof
|
|
62618
|
+
if (error instanceof TimebackApiError3 && error.status === 409) {} else {
|
|
62363
62619
|
throw error;
|
|
62364
62620
|
}
|
|
62365
62621
|
}
|
|
@@ -62659,7 +62915,7 @@ class TimebackAssessmentsService {
|
|
|
62659
62915
|
}
|
|
62660
62916
|
async requireIntegration(integrationId) {
|
|
62661
62917
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
62662
|
-
where: eq(gameTimebackIntegrations.id, integrationId)
|
|
62918
|
+
where: and(eq(gameTimebackIntegrations.id, integrationId), isActiveGameTimebackIntegrationStatus())
|
|
62663
62919
|
});
|
|
62664
62920
|
if (!integration) {
|
|
62665
62921
|
throw new NotFoundError(`Integration not found: ${integrationId}`);
|
|
@@ -62690,7 +62946,7 @@ class TimebackAssessmentsService {
|
|
|
62690
62946
|
}
|
|
62691
62947
|
});
|
|
62692
62948
|
} catch (error) {
|
|
62693
|
-
if (!(error instanceof
|
|
62949
|
+
if (!(error instanceof TimebackApiError3 && error.status === 409)) {
|
|
62694
62950
|
throw error;
|
|
62695
62951
|
}
|
|
62696
62952
|
}
|
|
@@ -62712,7 +62968,7 @@ class TimebackAssessmentsService {
|
|
|
62712
62968
|
}
|
|
62713
62969
|
});
|
|
62714
62970
|
} catch (error) {
|
|
62715
|
-
if (!(error instanceof
|
|
62971
|
+
if (!(error instanceof TimebackApiError3 && error.status === 409)) {
|
|
62716
62972
|
throw error;
|
|
62717
62973
|
}
|
|
62718
62974
|
}
|
|
@@ -62730,7 +62986,7 @@ class TimebackAssessmentsService {
|
|
|
62730
62986
|
}
|
|
62731
62987
|
});
|
|
62732
62988
|
} catch (error) {
|
|
62733
|
-
if (!(error instanceof
|
|
62989
|
+
if (!(error instanceof TimebackApiError3 && error.status === 409)) {
|
|
62734
62990
|
throw error;
|
|
62735
62991
|
}
|
|
62736
62992
|
}
|
|
@@ -62748,13 +63004,60 @@ class TimebackAssessmentsService {
|
|
|
62748
63004
|
}
|
|
62749
63005
|
var init_timeback_assessments_service = __esm(() => {
|
|
62750
63006
|
init_drizzle_orm();
|
|
63007
|
+
init_helpers_index();
|
|
62751
63008
|
init_tables_index();
|
|
62752
63009
|
init_spans();
|
|
62753
|
-
|
|
62754
|
-
|
|
63010
|
+
init_constants6();
|
|
63011
|
+
init_errors6();
|
|
62755
63012
|
init_utils6();
|
|
62756
63013
|
init_errors();
|
|
62757
63014
|
});
|
|
63015
|
+
function buildTimebackBaseConfigFromExistingConfig(config2) {
|
|
63016
|
+
return {
|
|
63017
|
+
organization: config2.organization,
|
|
63018
|
+
component: {
|
|
63019
|
+
...config2.component,
|
|
63020
|
+
title: ""
|
|
63021
|
+
},
|
|
63022
|
+
resource: {
|
|
63023
|
+
...config2.resource,
|
|
63024
|
+
title: ""
|
|
63025
|
+
},
|
|
63026
|
+
componentResource: {
|
|
63027
|
+
...config2.componentResource,
|
|
63028
|
+
title: ""
|
|
63029
|
+
}
|
|
63030
|
+
};
|
|
63031
|
+
}
|
|
63032
|
+
function getMasterableUnitsFromTimebackConfig(config2) {
|
|
63033
|
+
const playcademyMetadata = config2.resource.metadata?.playcademy;
|
|
63034
|
+
if (!isPlaycademyResourceMetadata(playcademyMetadata)) {
|
|
63035
|
+
return null;
|
|
63036
|
+
}
|
|
63037
|
+
return playcademyMetadata?.mastery?.masterableUnits ?? null;
|
|
63038
|
+
}
|
|
63039
|
+
function getTotalXpFromTimebackConfig(config2) {
|
|
63040
|
+
const courseMetadata = isCourseMetadata(config2.course.metadata) ? config2.course.metadata : undefined;
|
|
63041
|
+
if (typeof courseMetadata?.metrics?.totalXp === "number") {
|
|
63042
|
+
return courseMetadata.metrics.totalXp;
|
|
63043
|
+
}
|
|
63044
|
+
const resourceMetadata = config2.resource.metadata;
|
|
63045
|
+
if (isRecord2(resourceMetadata) && typeof resourceMetadata.xp === "number") {
|
|
63046
|
+
return resourceMetadata.xp;
|
|
63047
|
+
}
|
|
63048
|
+
return null;
|
|
63049
|
+
}
|
|
63050
|
+
function timebackConfigMatchesCreateIntegrationRequest(config2, request2) {
|
|
63051
|
+
const subject = config2.course.subjects[0];
|
|
63052
|
+
const grade = config2.course.grades[0];
|
|
63053
|
+
const requestedLevel = request2.level ?? deriveTimebackCourseLevelFromGrade(request2.grade);
|
|
63054
|
+
return subject === request2.subject && grade === request2.grade && config2.course.title === request2.title && config2.course.courseCode === request2.courseCode && config2.course.level === requestedLevel && (getTotalXpFromTimebackConfig(config2) ?? null) === request2.totalXp && (getMasterableUnitsFromTimebackConfig(config2) ?? null) === request2.masterableUnits;
|
|
63055
|
+
}
|
|
63056
|
+
var init_timeback_create_integration_util = __esm(() => {
|
|
63057
|
+
init_constants6();
|
|
63058
|
+
init_types2();
|
|
63059
|
+
init_timeback_util();
|
|
63060
|
+
});
|
|
62758
63061
|
async function promoteCompletedCourse({
|
|
62759
63062
|
db: db2,
|
|
62760
63063
|
client,
|
|
@@ -62763,7 +63066,7 @@ async function promoteCompletedCourse({
|
|
|
62763
63066
|
enrollments: prefetchedEnrollments
|
|
62764
63067
|
}) {
|
|
62765
63068
|
const subjectIntegrations = await db2.query.gameTimebackIntegrations.findMany({
|
|
62766
|
-
where: and(eq(gameTimebackIntegrations.gameId, currentIntegration.gameId), eq(gameTimebackIntegrations.subject, currentIntegration.subject))
|
|
63069
|
+
where: and(eq(gameTimebackIntegrations.gameId, currentIntegration.gameId), eq(gameTimebackIntegrations.subject, currentIntegration.subject), isActiveGameTimebackIntegrationStatus())
|
|
62767
63070
|
});
|
|
62768
63071
|
const nextIntegration = subjectIntegrations.filter((integration) => integration.grade > currentIntegration.grade).toSorted((left, right) => left.grade - right.grade)[0];
|
|
62769
63072
|
if (!nextIntegration) {
|
|
@@ -62818,19 +63121,68 @@ async function promoteCompletedCourse({
|
|
|
62818
63121
|
}
|
|
62819
63122
|
var init_timeback_promotion_util = __esm(() => {
|
|
62820
63123
|
init_drizzle_orm();
|
|
63124
|
+
init_helpers_index();
|
|
62821
63125
|
init_tables_index();
|
|
62822
63126
|
init_spans();
|
|
62823
63127
|
});
|
|
63128
|
+
function toGameTimebackIntegration(integration) {
|
|
63129
|
+
return {
|
|
63130
|
+
id: integration.id,
|
|
63131
|
+
gameId: integration.gameId,
|
|
63132
|
+
courseId: integration.courseId,
|
|
63133
|
+
grade: integration.grade,
|
|
63134
|
+
subject: integration.subject,
|
|
63135
|
+
totalXp: integration.totalXp ?? null,
|
|
63136
|
+
status: integration.status ?? ACTIVE_GAME_TIMEBACK_INTEGRATION_STATUS,
|
|
63137
|
+
deactivatedAt: integration.deactivatedAt ?? null,
|
|
63138
|
+
reactivatedAt: integration.reactivatedAt ?? null,
|
|
63139
|
+
createdAt: integration.createdAt,
|
|
63140
|
+
updatedAt: integration.updatedAt,
|
|
63141
|
+
lastVerifiedAt: integration.lastVerifiedAt ?? null
|
|
63142
|
+
};
|
|
63143
|
+
}
|
|
63144
|
+
function buildFallbackRemovedGameTimebackIntegration(integration) {
|
|
63145
|
+
if (!isTimebackSubject(integration.subject)) {
|
|
63146
|
+
throw new ValidationError(`Invalid subject "${integration.subject}"`);
|
|
63147
|
+
}
|
|
63148
|
+
if (!isTimebackGrade(integration.grade)) {
|
|
63149
|
+
throw new ValidationError(`Invalid grade "${integration.grade}"`);
|
|
63150
|
+
}
|
|
63151
|
+
return {
|
|
63152
|
+
integration: toGameTimebackIntegration(integration),
|
|
63153
|
+
title: `${integration.subject} ${formatGradeLabel(integration.grade)}`,
|
|
63154
|
+
courseCode: integration.courseId,
|
|
63155
|
+
subject: integration.subject,
|
|
63156
|
+
grade: integration.grade,
|
|
63157
|
+
totalXp: integration.totalXp ?? null,
|
|
63158
|
+
masterableUnits: null,
|
|
63159
|
+
removedAt: integration.deactivatedAt ?? null,
|
|
63160
|
+
metadata: null
|
|
63161
|
+
};
|
|
63162
|
+
}
|
|
63163
|
+
var init_timeback_removed_integration_util = __esm(() => {
|
|
63164
|
+
init_helpers_index();
|
|
63165
|
+
init_types2();
|
|
63166
|
+
init_timeback3();
|
|
63167
|
+
init_errors();
|
|
63168
|
+
});
|
|
63169
|
+
async function findGameTimebackIntegrationForUpdate(db2, condition) {
|
|
63170
|
+
const [integration] = await db2.select().from(gameTimebackIntegrations).where(condition).limit(1).for("update");
|
|
63171
|
+
return integration;
|
|
63172
|
+
}
|
|
62824
63173
|
var TimebackService;
|
|
62825
63174
|
var init_timeback_service = __esm(() => {
|
|
62826
63175
|
init_drizzle_orm();
|
|
62827
63176
|
init_src();
|
|
63177
|
+
init_helpers_index();
|
|
62828
63178
|
init_tables_index();
|
|
62829
63179
|
init_spans();
|
|
62830
63180
|
init_dist2();
|
|
62831
|
-
|
|
63181
|
+
init_types2();
|
|
62832
63182
|
init_errors();
|
|
63183
|
+
init_timeback_create_integration_util();
|
|
62833
63184
|
init_timeback_promotion_util();
|
|
63185
|
+
init_timeback_removed_integration_util();
|
|
62834
63186
|
init_timeback_util();
|
|
62835
63187
|
TimebackService = class TimebackService2 {
|
|
62836
63188
|
static HEARTBEAT_DEDUPE_TTL_MS = 300000;
|
|
@@ -63060,7 +63412,7 @@ var init_timeback_service = __esm(() => {
|
|
|
63060
63412
|
return [];
|
|
63061
63413
|
}
|
|
63062
63414
|
const integrations = await db2.query.gameTimebackIntegrations.findMany({
|
|
63063
|
-
where: inArray(gameTimebackIntegrations.courseId, courseIds)
|
|
63415
|
+
where: and(inArray(gameTimebackIntegrations.courseId, courseIds), isActiveGameTimebackIntegrationStatus())
|
|
63064
63416
|
});
|
|
63065
63417
|
return mapEnrollmentsToUserEnrollments(enrollments, integrations);
|
|
63066
63418
|
} catch (error) {
|
|
@@ -63085,6 +63437,7 @@ var init_timeback_service = __esm(() => {
|
|
|
63085
63437
|
const verboseData = [];
|
|
63086
63438
|
let integrationCreatedCount = 0;
|
|
63087
63439
|
let integrationUpdatedCount = 0;
|
|
63440
|
+
let integrationReactivatedCount = 0;
|
|
63088
63441
|
for (const courseConfig of courses) {
|
|
63089
63442
|
let applySuffix = function(text3) {
|
|
63090
63443
|
return suffix ? `${text3} ${suffix}` : text3;
|
|
@@ -63099,16 +63452,16 @@ var init_timeback_service = __esm(() => {
|
|
|
63099
63452
|
totalXp: derivedTotalXp,
|
|
63100
63453
|
masterableUnits: derivedMasterableUnits
|
|
63101
63454
|
} = courseConfig;
|
|
63102
|
-
if (!
|
|
63455
|
+
if (!isTimebackSubject(subjectInput)) {
|
|
63103
63456
|
throw new ValidationError(`Invalid subject "${subjectInput}"`);
|
|
63104
63457
|
}
|
|
63105
|
-
if (!
|
|
63458
|
+
if (!isTimebackGrade(grade)) {
|
|
63106
63459
|
throw new ValidationError(`Invalid grade "${grade}"`);
|
|
63107
63460
|
}
|
|
63108
63461
|
const subject = subjectInput;
|
|
63109
63462
|
const courseMetadata = isCourseMetadata(metadata2) ? metadata2 : undefined;
|
|
63110
63463
|
const totalXp = derivedTotalXp ?? courseMetadata?.metrics?.totalXp;
|
|
63111
|
-
const masterableUnits = derivedMasterableUnits ?? (
|
|
63464
|
+
const masterableUnits = derivedMasterableUnits ?? (isPlaycademyResourceMetadata(courseMetadata?.playcademy) ? courseMetadata?.playcademy?.mastery?.masterableUnits : undefined);
|
|
63112
63465
|
if (typeof totalXp !== "number") {
|
|
63113
63466
|
throw new ValidationError(`Course "${title}" is missing totalXp`);
|
|
63114
63467
|
}
|
|
@@ -63154,19 +63507,28 @@ var init_timeback_service = __esm(() => {
|
|
|
63154
63507
|
title: applySuffix(baseConfig.componentResource.title || "")
|
|
63155
63508
|
}
|
|
63156
63509
|
};
|
|
63157
|
-
const
|
|
63510
|
+
const matches = existing.filter((i2) => i2.grade === grade && i2.subject === subject);
|
|
63511
|
+
const existingIntegration = matches.find((i2) => i2.status !== DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS) ?? matches.toSorted((a, b) => (b.deactivatedAt?.getTime() ?? 0) - (a.deactivatedAt?.getTime() ?? 0))[0];
|
|
63158
63512
|
if (existingIntegration) {
|
|
63159
|
-
await
|
|
63160
|
-
|
|
63513
|
+
const { updated, revived } = await this.updateSetupIntegration({
|
|
63514
|
+
client,
|
|
63515
|
+
existingIntegration,
|
|
63516
|
+
fullConfig,
|
|
63517
|
+
subject,
|
|
63518
|
+
totalXp
|
|
63519
|
+
});
|
|
63161
63520
|
if (updated) {
|
|
63162
|
-
integrations.push(
|
|
63521
|
+
integrations.push(toGameTimebackIntegration(updated));
|
|
63163
63522
|
integrationUpdatedCount++;
|
|
63523
|
+
if (revived) {
|
|
63524
|
+
integrationReactivatedCount++;
|
|
63525
|
+
}
|
|
63164
63526
|
}
|
|
63165
63527
|
} else {
|
|
63166
63528
|
const result = await client.setup(fullConfig, { verbose });
|
|
63167
63529
|
const [integration] = await db2.insert(gameTimebackIntegrations).values({ gameId, courseId: result.courseId, grade, subject, totalXp }).returning();
|
|
63168
63530
|
if (integration) {
|
|
63169
|
-
const dto =
|
|
63531
|
+
const dto = toGameTimebackIntegration(integration);
|
|
63170
63532
|
integrations.push(dto);
|
|
63171
63533
|
integrationCreatedCount++;
|
|
63172
63534
|
if (verbose && result.verboseData) {
|
|
@@ -63178,7 +63540,8 @@ var init_timeback_service = __esm(() => {
|
|
|
63178
63540
|
setAttributes({
|
|
63179
63541
|
"app.timeback.integration_count": integrations.length,
|
|
63180
63542
|
"app.timeback.integration_created_count": integrationCreatedCount,
|
|
63181
|
-
"app.timeback.integration_updated_count": integrationUpdatedCount
|
|
63543
|
+
"app.timeback.integration_updated_count": integrationUpdatedCount,
|
|
63544
|
+
"app.timeback.integration_reactivated_count": integrationReactivatedCount
|
|
63182
63545
|
});
|
|
63183
63546
|
return {
|
|
63184
63547
|
integrations,
|
|
@@ -63186,19 +63549,221 @@ var init_timeback_service = __esm(() => {
|
|
|
63186
63549
|
};
|
|
63187
63550
|
});
|
|
63188
63551
|
}
|
|
63552
|
+
async updateSetupIntegration(args2) {
|
|
63553
|
+
const { client, existingIntegration, fullConfig, subject, totalXp } = args2;
|
|
63554
|
+
const revived = existingIntegration.status === DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS;
|
|
63555
|
+
await client.update(existingIntegration.courseId, fullConfig);
|
|
63556
|
+
if (revived) {
|
|
63557
|
+
await client.reactivateCourse(existingIntegration.courseId);
|
|
63558
|
+
}
|
|
63559
|
+
const now2 = new Date;
|
|
63560
|
+
const [updated] = await this.deps.db.update(gameTimebackIntegrations).set({
|
|
63561
|
+
subject,
|
|
63562
|
+
totalXp,
|
|
63563
|
+
updatedAt: now2,
|
|
63564
|
+
...revived && {
|
|
63565
|
+
status: ACTIVE_GAME_TIMEBACK_INTEGRATION_STATUS,
|
|
63566
|
+
deactivatedAt: null,
|
|
63567
|
+
reactivatedAt: now2
|
|
63568
|
+
}
|
|
63569
|
+
}).where(eq(gameTimebackIntegrations.id, existingIntegration.id)).returning();
|
|
63570
|
+
return { updated, revived };
|
|
63571
|
+
}
|
|
63572
|
+
async createIntegration(gameId, user, request2) {
|
|
63573
|
+
return this.withClientTelemetry(async () => {
|
|
63574
|
+
const client = this.requireClient();
|
|
63575
|
+
const db2 = this.deps.db;
|
|
63576
|
+
await this.deps.validateDeveloperAccess(user, gameId);
|
|
63577
|
+
setAttributes({
|
|
63578
|
+
"app.timeback.grade": request2.grade,
|
|
63579
|
+
"app.timeback.subject": request2.subject
|
|
63580
|
+
});
|
|
63581
|
+
const activeConflict = await db2.query.gameTimebackIntegrations.findFirst({
|
|
63582
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.grade, request2.grade), eq(gameTimebackIntegrations.subject, request2.subject), isActiveGameTimebackIntegrationStatus())
|
|
63583
|
+
});
|
|
63584
|
+
if (activeConflict) {
|
|
63585
|
+
throw new ConflictError(`A TimeBack integration already exists for ${request2.subject} Grade ${request2.grade}`);
|
|
63586
|
+
}
|
|
63587
|
+
const removedCandidates = await db2.query.gameTimebackIntegrations.findMany({
|
|
63588
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.grade, request2.grade), eq(gameTimebackIntegrations.subject, request2.subject), eq(gameTimebackIntegrations.status, DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS))
|
|
63589
|
+
});
|
|
63590
|
+
for (const candidate of removedCandidates) {
|
|
63591
|
+
const config2 = await client.getConfig(candidate.courseId).catch((error) => {
|
|
63592
|
+
addEvent("timeback.removed_integration_config_fetch_failed", {
|
|
63593
|
+
"app.game.id": gameId,
|
|
63594
|
+
"app.timeback.course_id": candidate.courseId,
|
|
63595
|
+
"exception.type": errorType(error),
|
|
63596
|
+
"app.error.message": errorMessage(error)
|
|
63597
|
+
});
|
|
63598
|
+
return null;
|
|
63599
|
+
});
|
|
63600
|
+
if (config2 && timebackConfigMatchesCreateIntegrationRequest(config2, request2)) {
|
|
63601
|
+
const updated = await db2.transaction(async (tx) => {
|
|
63602
|
+
const database = tx;
|
|
63603
|
+
const lockedCandidate = await findGameTimebackIntegrationForUpdate(database, eq(gameTimebackIntegrations.id, candidate.id));
|
|
63604
|
+
if (lockedCandidate?.status !== DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS) {
|
|
63605
|
+
throw new ConflictError(`A TimeBack integration already exists for ${request2.subject} Grade ${request2.grade}`);
|
|
63606
|
+
}
|
|
63607
|
+
const now2 = new Date;
|
|
63608
|
+
const [integration2] = await database.update(gameTimebackIntegrations).set({
|
|
63609
|
+
status: ACTIVE_GAME_TIMEBACK_INTEGRATION_STATUS,
|
|
63610
|
+
totalXp: request2.totalXp,
|
|
63611
|
+
deactivatedAt: null,
|
|
63612
|
+
reactivatedAt: now2,
|
|
63613
|
+
updatedAt: now2
|
|
63614
|
+
}).where(and(eq(gameTimebackIntegrations.id, candidate.id), eq(gameTimebackIntegrations.status, DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS))).returning();
|
|
63615
|
+
if (!integration2) {
|
|
63616
|
+
throw new NotFoundError("Timeback integration", candidate.id);
|
|
63617
|
+
}
|
|
63618
|
+
await client.reactivateCourse(candidate.courseId);
|
|
63619
|
+
return integration2;
|
|
63620
|
+
});
|
|
63621
|
+
setAttribute("app.timeback.course_create_mode", "reactivated-removed");
|
|
63622
|
+
return toGameTimebackIntegration(updated);
|
|
63623
|
+
}
|
|
63624
|
+
}
|
|
63625
|
+
const reference = await db2.query.gameTimebackIntegrations.findFirst({
|
|
63626
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), isActiveGameTimebackIntegrationStatus()),
|
|
63627
|
+
orderBy: (table8, { asc: asc2 }) => [asc2(table8.createdAt)]
|
|
63628
|
+
}) ?? await db2.query.gameTimebackIntegrations.findFirst({
|
|
63629
|
+
where: eq(gameTimebackIntegrations.gameId, gameId),
|
|
63630
|
+
orderBy: (table8, { asc: asc2 }) => [asc2(table8.createdAt)]
|
|
63631
|
+
});
|
|
63632
|
+
if (!reference) {
|
|
63633
|
+
throw new ValidationError("Run `playcademy timeback setup` before adding courses from the dashboard.");
|
|
63634
|
+
}
|
|
63635
|
+
const referenceConfig = await client.getConfig(reference.courseId);
|
|
63636
|
+
const result = await this.setupIntegration(gameId, {
|
|
63637
|
+
gameId,
|
|
63638
|
+
courses: [
|
|
63639
|
+
{
|
|
63640
|
+
title: request2.title,
|
|
63641
|
+
courseCode: request2.courseCode,
|
|
63642
|
+
subject: request2.subject,
|
|
63643
|
+
grade: request2.grade,
|
|
63644
|
+
level: request2.level ?? deriveTimebackCourseLevelFromGrade(request2.grade),
|
|
63645
|
+
totalXp: request2.totalXp,
|
|
63646
|
+
masterableUnits: request2.masterableUnits
|
|
63647
|
+
}
|
|
63648
|
+
],
|
|
63649
|
+
baseConfig: buildTimebackBaseConfigFromExistingConfig(referenceConfig)
|
|
63650
|
+
}, user);
|
|
63651
|
+
const integration = result.integrations[0];
|
|
63652
|
+
if (!integration) {
|
|
63653
|
+
throw new InternalError("TimeBack course creation did not return an integration");
|
|
63654
|
+
}
|
|
63655
|
+
setAttribute("app.timeback.course_create_mode", "created-new");
|
|
63656
|
+
return integration;
|
|
63657
|
+
});
|
|
63658
|
+
}
|
|
63189
63659
|
async getIntegrations(gameId, user) {
|
|
63190
63660
|
await this.deps.validateGameManagementAccess(user, gameId);
|
|
63191
63661
|
const rows = await this.deps.db.query.gameTimebackIntegrations.findMany({
|
|
63192
|
-
where: eq(gameTimebackIntegrations.gameId, gameId)
|
|
63662
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), isActiveGameTimebackIntegrationStatus())
|
|
63663
|
+
});
|
|
63664
|
+
return rows.map((row) => toGameTimebackIntegration(row));
|
|
63665
|
+
}
|
|
63666
|
+
async getRemovedIntegrations(gameId, user) {
|
|
63667
|
+
return this.withClientTelemetry(async () => {
|
|
63668
|
+
const client = this.requireClient();
|
|
63669
|
+
await this.deps.validateGameManagementAccess(user, gameId);
|
|
63670
|
+
const rows = await this.deps.db.query.gameTimebackIntegrations.findMany({
|
|
63671
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.status, DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS)),
|
|
63672
|
+
orderBy: (table8, { asc: asc2 }) => [asc2(table8.subject), asc2(table8.grade)]
|
|
63673
|
+
});
|
|
63674
|
+
setAttribute("app.timeback.removed_integration_count", rows.length);
|
|
63675
|
+
return Promise.all(rows.map(async (row) => {
|
|
63676
|
+
try {
|
|
63677
|
+
const config2 = await client.getConfig(row.courseId);
|
|
63678
|
+
return this.toRemovedGameTimebackIntegration(row, config2);
|
|
63679
|
+
} catch (error) {
|
|
63680
|
+
addEvent("timeback.removed_integration_config_fetch_failed", {
|
|
63681
|
+
"app.game.id": gameId,
|
|
63682
|
+
"app.timeback.course_id": row.courseId,
|
|
63683
|
+
"exception.type": errorType(error),
|
|
63684
|
+
"app.error.message": errorMessage(error)
|
|
63685
|
+
});
|
|
63686
|
+
return buildFallbackRemovedGameTimebackIntegration(row);
|
|
63687
|
+
}
|
|
63688
|
+
}));
|
|
63689
|
+
});
|
|
63690
|
+
}
|
|
63691
|
+
async deactivateCourse(gameId, courseId, user) {
|
|
63692
|
+
return this.withClientTelemetry(async () => {
|
|
63693
|
+
const client = this.requireClient();
|
|
63694
|
+
const db2 = this.deps.db;
|
|
63695
|
+
await this.deps.validateDeveloperAccess(user, gameId);
|
|
63696
|
+
const updated = await db2.transaction(async (tx) => {
|
|
63697
|
+
const database = tx;
|
|
63698
|
+
const integration = await findGameTimebackIntegrationForUpdate(database, and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus()));
|
|
63699
|
+
if (!integration) {
|
|
63700
|
+
throw new NotFoundError("Timeback course", `${gameId}:${courseId}`);
|
|
63701
|
+
}
|
|
63702
|
+
const activeEnrollments = await client.oneroster.enrollments.listByCourse(courseId, {
|
|
63703
|
+
role: "student",
|
|
63704
|
+
includeUsers: false
|
|
63705
|
+
});
|
|
63706
|
+
setAttribute("app.timeback.active_enrollment_count", activeEnrollments.length);
|
|
63707
|
+
if (activeEnrollments.length > 0) {
|
|
63708
|
+
throw new ConflictError("Cannot remove course with active enrollments", {
|
|
63709
|
+
courseId,
|
|
63710
|
+
activeEnrollmentCount: activeEnrollments.length
|
|
63711
|
+
});
|
|
63712
|
+
}
|
|
63713
|
+
const now2 = new Date;
|
|
63714
|
+
const [updatedIntegration] = await database.update(gameTimebackIntegrations).set({
|
|
63715
|
+
status: DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS,
|
|
63716
|
+
deactivatedAt: now2,
|
|
63717
|
+
updatedAt: now2
|
|
63718
|
+
}).where(and(eq(gameTimebackIntegrations.id, integration.id), isActiveGameTimebackIntegrationStatus())).returning();
|
|
63719
|
+
if (!updatedIntegration) {
|
|
63720
|
+
throw new NotFoundError("Timeback course", `${gameId}:${courseId}`);
|
|
63721
|
+
}
|
|
63722
|
+
await client.deactivateCourse(courseId);
|
|
63723
|
+
return updatedIntegration;
|
|
63724
|
+
});
|
|
63725
|
+
return toGameTimebackIntegration(updated);
|
|
63726
|
+
});
|
|
63727
|
+
}
|
|
63728
|
+
async reactivateCourse(gameId, courseId, user) {
|
|
63729
|
+
return this.withClientTelemetry(async () => {
|
|
63730
|
+
const client = this.requireClient();
|
|
63731
|
+
const db2 = this.deps.db;
|
|
63732
|
+
await this.deps.validateDeveloperAccess(user, gameId);
|
|
63733
|
+
const updated = await db2.transaction(async (tx) => {
|
|
63734
|
+
const database = tx;
|
|
63735
|
+
const integration = await findGameTimebackIntegrationForUpdate(database, and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), eq(gameTimebackIntegrations.status, DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS)));
|
|
63736
|
+
if (!integration) {
|
|
63737
|
+
throw new NotFoundError("Removed Timeback course", `${gameId}:${courseId}`);
|
|
63738
|
+
}
|
|
63739
|
+
const activeConflict = await database.query.gameTimebackIntegrations.findFirst({
|
|
63740
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.grade, integration.grade), eq(gameTimebackIntegrations.subject, integration.subject), isActiveGameTimebackIntegrationStatus())
|
|
63741
|
+
});
|
|
63742
|
+
if (activeConflict) {
|
|
63743
|
+
throw new ConflictError(`An active TimeBack course already exists for ${integration.subject} Grade ${integration.grade}`);
|
|
63744
|
+
}
|
|
63745
|
+
const now2 = new Date;
|
|
63746
|
+
const [updatedIntegration] = await database.update(gameTimebackIntegrations).set({
|
|
63747
|
+
status: ACTIVE_GAME_TIMEBACK_INTEGRATION_STATUS,
|
|
63748
|
+
deactivatedAt: null,
|
|
63749
|
+
reactivatedAt: now2,
|
|
63750
|
+
updatedAt: now2
|
|
63751
|
+
}).where(and(eq(gameTimebackIntegrations.id, integration.id), eq(gameTimebackIntegrations.status, DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS))).returning();
|
|
63752
|
+
if (!updatedIntegration) {
|
|
63753
|
+
throw new NotFoundError("Timeback course", `${gameId}:${courseId}`);
|
|
63754
|
+
}
|
|
63755
|
+
await client.reactivateCourse(courseId);
|
|
63756
|
+
return updatedIntegration;
|
|
63757
|
+
});
|
|
63758
|
+
return toGameTimebackIntegration(updated);
|
|
63193
63759
|
});
|
|
63194
|
-
return rows.map((row) => this.toGameTimebackIntegration(row));
|
|
63195
63760
|
}
|
|
63196
63761
|
async getIntegrationConfig(gameId, courseId, user) {
|
|
63197
63762
|
return this.withClientTelemetry(async () => {
|
|
63198
63763
|
const client = this.requireClient();
|
|
63199
63764
|
await this.deps.validateGameManagementAccess(user, gameId);
|
|
63200
63765
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
63201
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
63766
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus())
|
|
63202
63767
|
});
|
|
63203
63768
|
if (!integration) {
|
|
63204
63769
|
throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
|
|
@@ -63215,15 +63780,15 @@ var init_timeback_service = __esm(() => {
|
|
|
63215
63780
|
"app.timeback.course_id": courseId
|
|
63216
63781
|
});
|
|
63217
63782
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
63218
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
63783
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus())
|
|
63219
63784
|
});
|
|
63220
63785
|
if (!integration) {
|
|
63221
63786
|
throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
|
|
63222
63787
|
}
|
|
63223
63788
|
const timebackConfig = await client.getConfig(courseId);
|
|
63224
63789
|
const liveSubject = timebackConfig.course.subjects[0];
|
|
63225
|
-
const subject = patch.subject ?? (
|
|
63226
|
-
if (!
|
|
63790
|
+
const subject = patch.subject ?? (isTimebackSubject(liveSubject) ? liveSubject : integration.subject);
|
|
63791
|
+
if (!isTimebackSubject(subject)) {
|
|
63227
63792
|
throw new ValidationError(`Invalid subject "${subject}"`);
|
|
63228
63793
|
}
|
|
63229
63794
|
setAttributes({
|
|
@@ -63233,14 +63798,14 @@ var init_timeback_service = __esm(() => {
|
|
|
63233
63798
|
});
|
|
63234
63799
|
if (subject !== integration.subject) {
|
|
63235
63800
|
const subjectConflict = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
63236
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.grade, integration.grade), eq(gameTimebackIntegrations.subject, subject))
|
|
63801
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.grade, integration.grade), eq(gameTimebackIntegrations.subject, subject), isActiveGameTimebackIntegrationStatus())
|
|
63237
63802
|
});
|
|
63238
63803
|
if (subjectConflict && subjectConflict.id !== integration.id) {
|
|
63239
63804
|
throw new ValidationError(`A TimeBack integration already exists for ${subject} Grade ${integration.grade}`);
|
|
63240
63805
|
}
|
|
63241
63806
|
}
|
|
63242
|
-
const totalXp = "totalXp" in patch ? patch.totalXp ?? null :
|
|
63243
|
-
const masterableUnits = "masterableUnits" in patch ? patch.masterableUnits ?? null :
|
|
63807
|
+
const totalXp = "totalXp" in patch ? patch.totalXp ?? null : getTotalXpFromTimebackConfig(timebackConfig) ?? integration.totalXp ?? null;
|
|
63808
|
+
const masterableUnits = "masterableUnits" in patch ? patch.masterableUnits ?? null : getMasterableUnitsFromTimebackConfig(timebackConfig);
|
|
63244
63809
|
setAttributes({
|
|
63245
63810
|
"app.timeback.total_xp": totalXp,
|
|
63246
63811
|
"app.timeback.masterable_units": masterableUnits
|
|
@@ -63260,7 +63825,7 @@ var init_timeback_service = __esm(() => {
|
|
|
63260
63825
|
if (!updated) {
|
|
63261
63826
|
throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
|
|
63262
63827
|
}
|
|
63263
|
-
return
|
|
63828
|
+
return toGameTimebackIntegration(updated);
|
|
63264
63829
|
});
|
|
63265
63830
|
}
|
|
63266
63831
|
async verifyIntegration(gameId, user) {
|
|
@@ -63270,7 +63835,7 @@ var init_timeback_service = __esm(() => {
|
|
|
63270
63835
|
await this.deps.validateDeveloperAccess(user, gameId);
|
|
63271
63836
|
TimebackService2.recordIntegrationOperation("verify_integration");
|
|
63272
63837
|
const integrations = await db2.query.gameTimebackIntegrations.findMany({
|
|
63273
|
-
where: eq(gameTimebackIntegrations.gameId, gameId)
|
|
63838
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), isActiveGameTimebackIntegrationStatus())
|
|
63274
63839
|
});
|
|
63275
63840
|
if (integrations.length === 0) {
|
|
63276
63841
|
throw new NotFoundError("Timeback integration", gameId);
|
|
@@ -63283,7 +63848,7 @@ var init_timeback_service = __esm(() => {
|
|
|
63283
63848
|
const errors3 = Object.entries(resources).filter(([_2, r]) => !r.found).map(([name3]) => `${name3} not found`);
|
|
63284
63849
|
const status = allFound ? "success" : "error";
|
|
63285
63850
|
return {
|
|
63286
|
-
integration:
|
|
63851
|
+
integration: toGameTimebackIntegration({
|
|
63287
63852
|
...integration,
|
|
63288
63853
|
lastVerifiedAt: now2
|
|
63289
63854
|
}),
|
|
@@ -63292,7 +63857,7 @@ var init_timeback_service = __esm(() => {
|
|
|
63292
63857
|
...errors3.length > 0 && { errors: errors3 }
|
|
63293
63858
|
};
|
|
63294
63859
|
}));
|
|
63295
|
-
await db2.update(gameTimebackIntegrations).set({ lastVerifiedAt: now2 }).where(
|
|
63860
|
+
await db2.update(gameTimebackIntegrations).set({ lastVerifiedAt: now2 }).where(inArray(gameTimebackIntegrations.id, integrations.map((integration) => integration.id)));
|
|
63296
63861
|
const overallStatus = results.every((r) => r.status === "success") ? "success" : "error";
|
|
63297
63862
|
const missingResourceCount = results.reduce((count, result) => count + (result.errors?.length ?? 0), 0);
|
|
63298
63863
|
setAttributes({
|
|
@@ -63308,7 +63873,7 @@ var init_timeback_service = __esm(() => {
|
|
|
63308
63873
|
const client = this.requireClient();
|
|
63309
63874
|
await this.deps.validateDeveloperAccess(user, gameId);
|
|
63310
63875
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
63311
|
-
where: eq(gameTimebackIntegrations.gameId, gameId)
|
|
63876
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), isActiveGameTimebackIntegrationStatus())
|
|
63312
63877
|
});
|
|
63313
63878
|
if (!integration) {
|
|
63314
63879
|
throw new NotFoundError("Timeback integration", gameId);
|
|
@@ -63335,24 +63900,6 @@ var init_timeback_service = __esm(() => {
|
|
|
63335
63900
|
await db2.delete(gameTimebackIntegrations).where(eq(gameTimebackIntegrations.gameId, gameId));
|
|
63336
63901
|
});
|
|
63337
63902
|
}
|
|
63338
|
-
static getMasterableUnitsFromConfig(config2) {
|
|
63339
|
-
const playcademyMetadata = config2.resource.metadata?.playcademy;
|
|
63340
|
-
if (!isPlaycademyResourceMetadata2(playcademyMetadata)) {
|
|
63341
|
-
return null;
|
|
63342
|
-
}
|
|
63343
|
-
return playcademyMetadata?.mastery?.masterableUnits ?? null;
|
|
63344
|
-
}
|
|
63345
|
-
static getTotalXpFromConfig(config2) {
|
|
63346
|
-
const courseMetadata = isCourseMetadata(config2.course.metadata) ? config2.course.metadata : undefined;
|
|
63347
|
-
if (typeof courseMetadata?.metrics?.totalXp === "number") {
|
|
63348
|
-
return courseMetadata.metrics.totalXp;
|
|
63349
|
-
}
|
|
63350
|
-
const resourceMetadata = config2.resource.metadata;
|
|
63351
|
-
if (isRecord2(resourceMetadata) && typeof resourceMetadata.xp === "number") {
|
|
63352
|
-
return resourceMetadata.xp;
|
|
63353
|
-
}
|
|
63354
|
-
return null;
|
|
63355
|
-
}
|
|
63356
63903
|
static patchCourseMetadata(metadata2, totalXp, options) {
|
|
63357
63904
|
const nextMetadata = isRecord2(metadata2) ? { ...metadata2 } : {};
|
|
63358
63905
|
const currentMetrics = isRecord2(nextMetadata.metrics) ? nextMetadata.metrics : {};
|
|
@@ -63475,31 +64022,29 @@ var init_timeback_service = __esm(() => {
|
|
|
63475
64022
|
}
|
|
63476
64023
|
};
|
|
63477
64024
|
}
|
|
63478
|
-
|
|
64025
|
+
toRemovedGameTimebackIntegration(integration, config2) {
|
|
64026
|
+
const grade = config2.course.grades[0] ?? integration.grade;
|
|
64027
|
+
if (!isTimebackGrade(grade)) {
|
|
64028
|
+
throw new ValidationError(`Invalid grade "${grade}"`);
|
|
64029
|
+
}
|
|
63479
64030
|
return {
|
|
63480
|
-
|
|
63481
|
-
|
|
63482
|
-
|
|
63483
|
-
grade: integration.grade,
|
|
63484
|
-
subject: integration.subject,
|
|
63485
|
-
totalXp: integration.totalXp ?? null,
|
|
63486
|
-
createdAt: integration.createdAt,
|
|
63487
|
-
updatedAt: integration.updatedAt,
|
|
63488
|
-
lastVerifiedAt: integration.lastVerifiedAt ?? null
|
|
64031
|
+
...this.toGameTimebackIntegrationConfig(integration, config2),
|
|
64032
|
+
grade,
|
|
64033
|
+
removedAt: integration.deactivatedAt ?? null
|
|
63489
64034
|
};
|
|
63490
64035
|
}
|
|
63491
64036
|
toGameTimebackIntegrationConfig(integration, config2) {
|
|
63492
64037
|
const subject = config2.course.subjects[0] ?? integration.subject;
|
|
63493
|
-
if (!
|
|
64038
|
+
if (!isTimebackSubject(subject)) {
|
|
63494
64039
|
throw new ValidationError(`Invalid subject "${subject}"`);
|
|
63495
64040
|
}
|
|
63496
64041
|
return {
|
|
63497
|
-
integration:
|
|
64042
|
+
integration: toGameTimebackIntegration(integration),
|
|
63498
64043
|
title: config2.course.title,
|
|
63499
64044
|
courseCode: config2.course.courseCode,
|
|
63500
64045
|
subject,
|
|
63501
|
-
totalXp:
|
|
63502
|
-
masterableUnits:
|
|
64046
|
+
totalXp: getTotalXpFromTimebackConfig(config2) ?? integration.totalXp ?? null,
|
|
64047
|
+
masterableUnits: getMasterableUnitsFromTimebackConfig(config2),
|
|
63503
64048
|
metadata: isCourseMetadata(config2.course.metadata) ? config2.course.metadata : null
|
|
63504
64049
|
};
|
|
63505
64050
|
}
|
|
@@ -63531,7 +64076,7 @@ var init_timeback_service = __esm(() => {
|
|
|
63531
64076
|
});
|
|
63532
64077
|
await this.deps.validateDeveloperAccess(user, gameId);
|
|
63533
64078
|
const integration = await db2.query.gameTimebackIntegrations.findFirst({
|
|
63534
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.grade, activityData.grade), eq(gameTimebackIntegrations.subject, activityData.subject))
|
|
64079
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.grade, activityData.grade), eq(gameTimebackIntegrations.subject, activityData.subject), isActiveGameTimebackIntegrationStatus())
|
|
63535
64080
|
});
|
|
63536
64081
|
if (!integration) {
|
|
63537
64082
|
throw new NotFoundError(`Timeback integration for game (grade ${activityData.grade}, subject ${activityData.subject})`);
|
|
@@ -63592,9 +64137,7 @@ var init_timeback_service = __esm(() => {
|
|
|
63592
64137
|
"app.timeback.xp_awarded": result.xpAwarded,
|
|
63593
64138
|
"app.timeback.mastered_units_requested": masteredUnits,
|
|
63594
64139
|
"app.timeback.mastered_units_absolute_requested": masteredUnitsAbsolute,
|
|
63595
|
-
"app.timeback.mastered_units_applied": result.masteredUnitsApplied
|
|
63596
|
-
"app.timeback.score_status": result.scoreStatus,
|
|
63597
|
-
"app.timeback.in_progress": result.inProgress
|
|
64140
|
+
"app.timeback.mastered_units_applied": result.masteredUnitsApplied
|
|
63598
64141
|
});
|
|
63599
64142
|
return {
|
|
63600
64143
|
status: "ok",
|
|
@@ -63602,8 +64145,6 @@ var init_timeback_service = __esm(() => {
|
|
|
63602
64145
|
xpAwarded: result.xpAwarded,
|
|
63603
64146
|
masteredUnits: result.masteredUnitsApplied,
|
|
63604
64147
|
pctCompleteApp: result.pctCompleteApp,
|
|
63605
|
-
scoreStatus: result.scoreStatus,
|
|
63606
|
-
inProgress: result.inProgress,
|
|
63607
64148
|
...result.warnings ? { warnings: result.warnings } : {}
|
|
63608
64149
|
};
|
|
63609
64150
|
}
|
|
@@ -63616,7 +64157,7 @@ var init_timeback_service = __esm(() => {
|
|
|
63616
64157
|
const client = this.requireClient();
|
|
63617
64158
|
const db2 = this.deps.db;
|
|
63618
64159
|
const integrations = await db2.query.gameTimebackIntegrations.findMany({
|
|
63619
|
-
where: subject ? and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.subject, subject)) : eq(gameTimebackIntegrations.gameId, gameId)
|
|
64160
|
+
where: subject ? and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.subject, subject), isActiveGameTimebackIntegrationStatus()) : and(eq(gameTimebackIntegrations.gameId, gameId), isActiveGameTimebackIntegrationStatus())
|
|
63620
64161
|
});
|
|
63621
64162
|
if (integrations.length === 0) {
|
|
63622
64163
|
throw new NotFoundError(subject ? `Timeback integrations for game (subject ${subject})` : "Timeback integrations for game");
|
|
@@ -63798,7 +64339,7 @@ var init_timeback_service = __esm(() => {
|
|
|
63798
64339
|
}
|
|
63799
64340
|
const pendingHeartbeat = (async () => {
|
|
63800
64341
|
const integration = await db2.query.gameTimebackIntegrations.findFirst({
|
|
63801
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.grade, activityData.grade), eq(gameTimebackIntegrations.subject, activityData.subject))
|
|
64342
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.grade, activityData.grade), eq(gameTimebackIntegrations.subject, activityData.subject), isActiveGameTimebackIntegrationStatus())
|
|
63802
64343
|
});
|
|
63803
64344
|
if (!integration) {
|
|
63804
64345
|
throw new NotFoundError(`Timeback integration for game (grade ${activityData.grade}, subject ${activityData.subject})`);
|
|
@@ -63846,7 +64387,10 @@ var init_timeback_service = __esm(() => {
|
|
|
63846
64387
|
let courseIds = [];
|
|
63847
64388
|
if (options?.gameId) {
|
|
63848
64389
|
await this.deps.validateDeveloperAccess(user, options.gameId);
|
|
63849
|
-
const conditions2 = [
|
|
64390
|
+
const conditions2 = [
|
|
64391
|
+
eq(gameTimebackIntegrations.gameId, options.gameId),
|
|
64392
|
+
isActiveGameTimebackIntegrationStatus()
|
|
64393
|
+
];
|
|
63850
64394
|
if (options.grade !== undefined && options.subject) {
|
|
63851
64395
|
conditions2.push(eq(gameTimebackIntegrations.grade, options.grade));
|
|
63852
64396
|
conditions2.push(eq(gameTimebackIntegrations.subject, options.subject));
|
|
@@ -63873,7 +64417,10 @@ var init_timeback_service = __esm(() => {
|
|
|
63873
64417
|
const client = this.requireClient();
|
|
63874
64418
|
const db2 = this.deps.db;
|
|
63875
64419
|
await this.deps.validateDeveloperAccess(user, options.gameId);
|
|
63876
|
-
const conditions2 = [
|
|
64420
|
+
const conditions2 = [
|
|
64421
|
+
eq(gameTimebackIntegrations.gameId, options.gameId),
|
|
64422
|
+
isActiveGameTimebackIntegrationStatus()
|
|
64423
|
+
];
|
|
63877
64424
|
if (options.grade !== undefined && options.subject) {
|
|
63878
64425
|
conditions2.push(eq(gameTimebackIntegrations.grade, options.grade));
|
|
63879
64426
|
conditions2.push(eq(gameTimebackIntegrations.subject, options.subject));
|
|
@@ -63900,7 +64447,7 @@ var init_timeback_service = __esm(() => {
|
|
|
63900
64447
|
const db2 = this.deps.db;
|
|
63901
64448
|
await this.deps.validateDeveloperAccess(user, options.gameId);
|
|
63902
64449
|
const integration = await db2.query.gameTimebackIntegrations.findFirst({
|
|
63903
|
-
where: and(eq(gameTimebackIntegrations.gameId, options.gameId), eq(gameTimebackIntegrations.subject, options.subject))
|
|
64450
|
+
where: and(eq(gameTimebackIntegrations.gameId, options.gameId), eq(gameTimebackIntegrations.subject, options.subject), isActiveGameTimebackIntegrationStatus())
|
|
63904
64451
|
});
|
|
63905
64452
|
if (!integration) {
|
|
63906
64453
|
throw new ValidationError(`Subject "${options.subject}" is not configured for game ${options.gameId}`);
|
|
@@ -64767,7 +65314,7 @@ class UserService {
|
|
|
64767
65314
|
return [];
|
|
64768
65315
|
}
|
|
64769
65316
|
const integrations = await this.deps.db.query.gameTimebackIntegrations.findMany({
|
|
64770
|
-
where: inArray(gameTimebackIntegrations.courseId, courseIds)
|
|
65317
|
+
where: and(inArray(gameTimebackIntegrations.courseId, courseIds), isActiveGameTimebackIntegrationStatus())
|
|
64771
65318
|
});
|
|
64772
65319
|
return mapEnrollmentsToUserEnrollments(enrollments, integrations);
|
|
64773
65320
|
} catch (error) {
|
|
@@ -64789,6 +65336,7 @@ class UserService {
|
|
|
64789
65336
|
var init_user_service = __esm(() => {
|
|
64790
65337
|
init_drizzle_orm();
|
|
64791
65338
|
init_src();
|
|
65339
|
+
init_helpers_index();
|
|
64792
65340
|
init_tables_index();
|
|
64793
65341
|
init_spans();
|
|
64794
65342
|
init_errors();
|
|
@@ -81189,7 +81737,7 @@ function extractTablesRelationalConfig2(schema5, configHelpers) {
|
|
|
81189
81737
|
return { tables: tablesConfig, tableNamesMap };
|
|
81190
81738
|
}
|
|
81191
81739
|
function relations3(table62, relations22) {
|
|
81192
|
-
return new Relations2(table62, (
|
|
81740
|
+
return new Relations2(table62, (helpers3) => Object.fromEntries(Object.entries(relations22(helpers3)).map(([key, value]) => [
|
|
81193
81741
|
key,
|
|
81194
81742
|
value.withFieldName(key)
|
|
81195
81743
|
])));
|
|
@@ -82452,7 +83000,7 @@ var errorMap2;
|
|
|
82452
83000
|
var en_default2;
|
|
82453
83001
|
var init_en2;
|
|
82454
83002
|
var overrideErrorMap2;
|
|
82455
|
-
var
|
|
83003
|
+
var init_errors8;
|
|
82456
83004
|
var makeIssue2;
|
|
82457
83005
|
var ParseStatus2;
|
|
82458
83006
|
var INVALID2;
|
|
@@ -88689,7 +89237,7 @@ See: https://github.com/isaacs/node-glob/issues/167`);
|
|
|
88689
89237
|
en_default2 = errorMap2;
|
|
88690
89238
|
}
|
|
88691
89239
|
});
|
|
88692
|
-
|
|
89240
|
+
init_errors8 = __esm7({
|
|
88693
89241
|
"../node_modules/.pnpm/zod@3.25.42/node_modules/zod/dist/esm/v3/errors.js"() {
|
|
88694
89242
|
init_en2();
|
|
88695
89243
|
overrideErrorMap2 = en_default2;
|
|
@@ -88697,7 +89245,7 @@ See: https://github.com/isaacs/node-glob/issues/167`);
|
|
|
88697
89245
|
});
|
|
88698
89246
|
init_parseUtil2 = __esm7({
|
|
88699
89247
|
"../node_modules/.pnpm/zod@3.25.42/node_modules/zod/dist/esm/v3/helpers/parseUtil.js"() {
|
|
88700
|
-
|
|
89248
|
+
init_errors8();
|
|
88701
89249
|
init_en2();
|
|
88702
89250
|
makeIssue2 = (params) => {
|
|
88703
89251
|
const { data, path: path22, errorMaps, issueData } = params;
|
|
@@ -88803,7 +89351,7 @@ See: https://github.com/isaacs/node-glob/issues/167`);
|
|
|
88803
89351
|
init_types5 = __esm7({
|
|
88804
89352
|
"../node_modules/.pnpm/zod@3.25.42/node_modules/zod/dist/esm/v3/types.js"() {
|
|
88805
89353
|
init_ZodError2();
|
|
88806
|
-
|
|
89354
|
+
init_errors8();
|
|
88807
89355
|
init_errorUtil2();
|
|
88808
89356
|
init_parseUtil2();
|
|
88809
89357
|
init_util2();
|
|
@@ -91961,7 +92509,7 @@ See: https://github.com/isaacs/node-glob/issues/167`);
|
|
|
91961
92509
|
});
|
|
91962
92510
|
init_external2 = __esm7({
|
|
91963
92511
|
"../node_modules/.pnpm/zod@3.25.42/node_modules/zod/dist/esm/v3/external.js"() {
|
|
91964
|
-
|
|
92512
|
+
init_errors8();
|
|
91965
92513
|
init_parseUtil2();
|
|
91966
92514
|
init_typeAliases2();
|
|
91967
92515
|
init_util2();
|
|
@@ -122615,7 +123163,7 @@ async function seedTimebackIntegrations(db2, gameId, courses) {
|
|
|
122615
123163
|
const courseId = course.courseId || `mock-${course.subject.toLowerCase()}-g${course.grade}`;
|
|
122616
123164
|
try {
|
|
122617
123165
|
const existing = await db2.query.gameTimebackIntegrations.findFirst({
|
|
122618
|
-
where: (table9, { and: and3, eq: eq3 }) => and3(eq3(table9.gameId, gameId), eq3(table9.grade, course.grade), eq3(table9.subject, course.subject))
|
|
123166
|
+
where: (table9, { and: and3, eq: eq3 }) => and3(eq3(table9.gameId, gameId), eq3(table9.grade, course.grade), eq3(table9.subject, course.subject), isActiveGameTimebackIntegrationStatus(table9.status))
|
|
122619
123167
|
});
|
|
122620
123168
|
if (!existing) {
|
|
122621
123169
|
await db2.insert(gameTimebackIntegrations).values({
|
|
@@ -122635,6 +123183,7 @@ async function seedTimebackIntegrations(db2, gameId, courses) {
|
|
|
122635
123183
|
return seededCount;
|
|
122636
123184
|
}
|
|
122637
123185
|
var init_timeback5 = __esm(() => {
|
|
123186
|
+
init_helpers_index();
|
|
122638
123187
|
init_tables_index();
|
|
122639
123188
|
init_config();
|
|
122640
123189
|
});
|
|
@@ -123840,6 +124389,7 @@ var init_game_member_controller = __esm(() => {
|
|
|
123840
124389
|
var list2;
|
|
123841
124390
|
var listAccessible;
|
|
123842
124391
|
var getSubjects;
|
|
124392
|
+
var getTimebackSummaries;
|
|
123843
124393
|
var getById;
|
|
123844
124394
|
var getBySlug;
|
|
123845
124395
|
var getManifest;
|
|
@@ -123855,6 +124405,7 @@ var init_game_controller = __esm(() => {
|
|
|
123855
124405
|
list2 = requireNonAnonymous(async (ctx) => ctx.services.game.list(ctx.user));
|
|
123856
124406
|
listAccessible = requireNonAnonymous(async (ctx) => ctx.services.game.listAccessible(ctx.user));
|
|
123857
124407
|
getSubjects = requireNonAnonymous(async (ctx) => ctx.services.game.getSubjects());
|
|
124408
|
+
getTimebackSummaries = requireGameManagementAccess(async (ctx) => ctx.services.game.getTimebackSummaries(ctx.user));
|
|
123858
124409
|
getById = requireNonAnonymous(async (ctx) => {
|
|
123859
124410
|
const gameId = requireGameId(ctx.params.gameId);
|
|
123860
124411
|
return ctx.services.game.getById(gameId, ctx.user);
|
|
@@ -123902,6 +124453,7 @@ var init_game_controller = __esm(() => {
|
|
|
123902
124453
|
list: list2,
|
|
123903
124454
|
listAccessible,
|
|
123904
124455
|
getSubjects,
|
|
124456
|
+
getTimebackSummaries,
|
|
123905
124457
|
getById,
|
|
123906
124458
|
getManifest,
|
|
123907
124459
|
getBySlug,
|
|
@@ -124305,7 +124857,11 @@ var getUserEnrollments;
|
|
|
124305
124857
|
var getUserById;
|
|
124306
124858
|
var setupIntegration;
|
|
124307
124859
|
var getIntegrations;
|
|
124860
|
+
var getRemovedIntegrations;
|
|
124861
|
+
var createIntegration;
|
|
124308
124862
|
var updateIntegration;
|
|
124863
|
+
var deactivateCourse;
|
|
124864
|
+
var reactivateCourse;
|
|
124309
124865
|
var getIntegrationConfig;
|
|
124310
124866
|
var verifyIntegration;
|
|
124311
124867
|
var getConfig;
|
|
@@ -124403,6 +124959,27 @@ var init_timeback_controller = __esm(() => {
|
|
|
124403
124959
|
}
|
|
124404
124960
|
return ctx.services.timeback.getIntegrations(gameId, ctx.user);
|
|
124405
124961
|
});
|
|
124962
|
+
getRemovedIntegrations = requireGameManagementAccess(async (ctx) => {
|
|
124963
|
+
const gameId = ctx.params.gameId;
|
|
124964
|
+
if (!gameId) {
|
|
124965
|
+
throw ApiError.badRequest("Missing gameId");
|
|
124966
|
+
}
|
|
124967
|
+
if (!isValidUUID(gameId)) {
|
|
124968
|
+
throw ApiError.unprocessableEntity("Invalid gameId format");
|
|
124969
|
+
}
|
|
124970
|
+
return ctx.services.timeback.getRemovedIntegrations(gameId, ctx.user);
|
|
124971
|
+
});
|
|
124972
|
+
createIntegration = requireDeveloper(async (ctx) => {
|
|
124973
|
+
const gameId = ctx.params.gameId;
|
|
124974
|
+
if (!gameId) {
|
|
124975
|
+
throw ApiError.badRequest("Missing gameId");
|
|
124976
|
+
}
|
|
124977
|
+
if (!isValidUUID(gameId)) {
|
|
124978
|
+
throw ApiError.unprocessableEntity("Invalid gameId format");
|
|
124979
|
+
}
|
|
124980
|
+
const body2 = await parseRequestBody(ctx.request, CreateGameTimebackIntegrationRequestSchema);
|
|
124981
|
+
return ctx.services.timeback.createIntegration(gameId, ctx.user, body2);
|
|
124982
|
+
});
|
|
124406
124983
|
updateIntegration = requireDeveloper(async (ctx) => {
|
|
124407
124984
|
const { gameId, courseId } = ctx.params;
|
|
124408
124985
|
if (!gameId || !courseId) {
|
|
@@ -124414,6 +124991,26 @@ var init_timeback_controller = __esm(() => {
|
|
|
124414
124991
|
const body2 = await parseRequestBody(ctx.request, UpdateGameTimebackIntegrationRequestSchema);
|
|
124415
124992
|
return ctx.services.timeback.updateIntegration(gameId, courseId, ctx.user, body2);
|
|
124416
124993
|
});
|
|
124994
|
+
deactivateCourse = requireDeveloper(async (ctx) => {
|
|
124995
|
+
const { gameId, courseId } = ctx.params;
|
|
124996
|
+
if (!gameId || !courseId) {
|
|
124997
|
+
throw ApiError.badRequest("Missing gameId or courseId parameter");
|
|
124998
|
+
}
|
|
124999
|
+
if (!isValidUUID(gameId)) {
|
|
125000
|
+
throw ApiError.unprocessableEntity("Invalid gameId format");
|
|
125001
|
+
}
|
|
125002
|
+
return ctx.services.timeback.deactivateCourse(gameId, courseId, ctx.user);
|
|
125003
|
+
});
|
|
125004
|
+
reactivateCourse = requireDeveloper(async (ctx) => {
|
|
125005
|
+
const { gameId, courseId } = ctx.params;
|
|
125006
|
+
if (!gameId || !courseId) {
|
|
125007
|
+
throw ApiError.badRequest("Missing gameId or courseId parameter");
|
|
125008
|
+
}
|
|
125009
|
+
if (!isValidUUID(gameId)) {
|
|
125010
|
+
throw ApiError.unprocessableEntity("Invalid gameId format");
|
|
125011
|
+
}
|
|
125012
|
+
return ctx.services.timeback.reactivateCourse(gameId, courseId, ctx.user);
|
|
125013
|
+
});
|
|
124417
125014
|
getIntegrationConfig = requireGameManagementAccess(async (ctx) => {
|
|
124418
125015
|
const { gameId, courseId } = ctx.params;
|
|
124419
125016
|
if (!gameId || !courseId) {
|
|
@@ -124563,11 +125160,11 @@ var init_timeback_controller = __esm(() => {
|
|
|
124563
125160
|
let subject;
|
|
124564
125161
|
if (gradeParam !== null && subjectParam !== null) {
|
|
124565
125162
|
const parsedGrade = parseInt(gradeParam, 10);
|
|
124566
|
-
if (!
|
|
125163
|
+
if (!isTimebackGrade2(parsedGrade)) {
|
|
124567
125164
|
throw ApiError.badRequest(`Invalid grade: ${gradeParam}. Valid grades: ${TIMEBACK_GRADES.join(", ")}`);
|
|
124568
125165
|
}
|
|
124569
|
-
if (!
|
|
124570
|
-
throw ApiError.badRequest(`Invalid subject: ${subjectParam}. Valid subjects: ${
|
|
125166
|
+
if (!isTimebackSubject2(subjectParam)) {
|
|
125167
|
+
throw ApiError.badRequest(`Invalid subject: ${subjectParam}. Valid subjects: ${TIMEBACK_SUBJECTS2.join(", ")}`);
|
|
124571
125168
|
}
|
|
124572
125169
|
grade = parsedGrade;
|
|
124573
125170
|
subject = subjectParam;
|
|
@@ -124603,11 +125200,11 @@ var init_timeback_controller = __esm(() => {
|
|
|
124603
125200
|
let subject;
|
|
124604
125201
|
if (gradeParam !== null && subjectParam !== null) {
|
|
124605
125202
|
const parsedGrade = parseInt(gradeParam, 10);
|
|
124606
|
-
if (!
|
|
125203
|
+
if (!isTimebackGrade2(parsedGrade)) {
|
|
124607
125204
|
throw ApiError.badRequest(`Invalid grade: ${gradeParam}. Valid grades: ${TIMEBACK_GRADES.join(", ")}`);
|
|
124608
125205
|
}
|
|
124609
|
-
if (!
|
|
124610
|
-
throw ApiError.badRequest(`Invalid subject: ${subjectParam}. Valid subjects: ${
|
|
125206
|
+
if (!isTimebackSubject2(subjectParam)) {
|
|
125207
|
+
throw ApiError.badRequest(`Invalid subject: ${subjectParam}. Valid subjects: ${TIMEBACK_SUBJECTS2.join(", ")}`);
|
|
124611
125208
|
}
|
|
124612
125209
|
grade = parsedGrade;
|
|
124613
125210
|
subject = subjectParam;
|
|
@@ -124637,8 +125234,8 @@ var init_timeback_controller = __esm(() => {
|
|
|
124637
125234
|
if (!subjectParam) {
|
|
124638
125235
|
throw ApiError.badRequest("Missing required subject query parameter");
|
|
124639
125236
|
}
|
|
124640
|
-
if (!
|
|
124641
|
-
throw ApiError.badRequest(`Invalid subject: ${subjectParam}. Valid subjects: ${
|
|
125237
|
+
if (!isTimebackSubject2(subjectParam)) {
|
|
125238
|
+
throw ApiError.badRequest(`Invalid subject: ${subjectParam}. Valid subjects: ${TIMEBACK_SUBJECTS2.join(", ")}`);
|
|
124642
125239
|
}
|
|
124643
125240
|
return ctx.services.timeback.getStudentHighestGradeMastered(timebackId, ctx.user, {
|
|
124644
125241
|
gameId,
|
|
@@ -124949,7 +125546,11 @@ var init_timeback_controller = __esm(() => {
|
|
|
124949
125546
|
getUserById,
|
|
124950
125547
|
setupIntegration,
|
|
124951
125548
|
getIntegrations,
|
|
125549
|
+
getRemovedIntegrations,
|
|
125550
|
+
createIntegration,
|
|
124952
125551
|
updateIntegration,
|
|
125552
|
+
deactivateCourse,
|
|
125553
|
+
reactivateCourse,
|
|
124953
125554
|
getIntegrationConfig,
|
|
124954
125555
|
verifyIntegration,
|
|
124955
125556
|
getConfig,
|
|
@@ -125909,12 +126510,12 @@ var init_timeback7 = __esm(() => {
|
|
|
125909
126510
|
}
|
|
125910
126511
|
if (gradeParam !== null && subjectParam !== null) {
|
|
125911
126512
|
const grade = parseInt(gradeParam, 10);
|
|
125912
|
-
if (!Number.isFinite(grade) || !
|
|
126513
|
+
if (!Number.isFinite(grade) || !isTimebackGrade2(grade)) {
|
|
125913
126514
|
const error2 = ApiError.badRequest(`Invalid grade: ${gradeParam}. Valid grades: ${TIMEBACK_GRADES.join(", ")}`);
|
|
125914
126515
|
return c2.json(createErrorResponse(error2), error2.status);
|
|
125915
126516
|
}
|
|
125916
|
-
if (!
|
|
125917
|
-
const error2 = ApiError.badRequest(`Invalid subject: ${subjectParam}. Valid subjects: ${
|
|
126517
|
+
if (!isTimebackSubject2(subjectParam)) {
|
|
126518
|
+
const error2 = ApiError.badRequest(`Invalid subject: ${subjectParam}. Valid subjects: ${TIMEBACK_SUBJECTS2.join(", ")}`);
|
|
125918
126519
|
return c2.json(createErrorResponse(error2), error2.status);
|
|
125919
126520
|
}
|
|
125920
126521
|
enrollments = enrollments.filter((e) => e.grade === grade && e.subject === subjectParam);
|
|
@@ -125967,12 +126568,12 @@ var init_timeback7 = __esm(() => {
|
|
|
125967
126568
|
}
|
|
125968
126569
|
if (gradeParam !== null && subjectParam !== null) {
|
|
125969
126570
|
const grade = parseInt(gradeParam, 10);
|
|
125970
|
-
if (!Number.isFinite(grade) || !
|
|
126571
|
+
if (!Number.isFinite(grade) || !isTimebackGrade2(grade)) {
|
|
125971
126572
|
const error2 = ApiError.badRequest(`Invalid grade: ${gradeParam}. Valid grades: ${TIMEBACK_GRADES.join(", ")}`);
|
|
125972
126573
|
return c2.json(createErrorResponse(error2), error2.status);
|
|
125973
126574
|
}
|
|
125974
|
-
if (!
|
|
125975
|
-
const error2 = ApiError.badRequest(`Invalid subject: ${subjectParam}. Valid subjects: ${
|
|
126575
|
+
if (!isTimebackSubject2(subjectParam)) {
|
|
126576
|
+
const error2 = ApiError.badRequest(`Invalid subject: ${subjectParam}. Valid subjects: ${TIMEBACK_SUBJECTS2.join(", ")}`);
|
|
125976
126577
|
return c2.json(createErrorResponse(error2), error2.status);
|
|
125977
126578
|
}
|
|
125978
126579
|
enrollments = enrollments.filter((e) => e.grade === grade && e.subject === subjectParam);
|
|
@@ -126021,8 +126622,8 @@ var init_timeback7 = __esm(() => {
|
|
|
126021
126622
|
const error2 = ApiError.badRequest("Missing required gameId query parameter");
|
|
126022
126623
|
return c2.json(createErrorResponse(error2), error2.status);
|
|
126023
126624
|
}
|
|
126024
|
-
if (!
|
|
126025
|
-
const error2 = ApiError.badRequest(`Invalid subject: ${subject}. Valid subjects: ${
|
|
126625
|
+
if (!isTimebackSubject2(subject)) {
|
|
126626
|
+
const error2 = ApiError.badRequest(`Invalid subject: ${subject}. Valid subjects: ${TIMEBACK_SUBJECTS2.join(", ")}`);
|
|
126026
126627
|
return c2.json(createErrorResponse(error2), error2.status);
|
|
126027
126628
|
}
|
|
126028
126629
|
const db2 = c2.get("db");
|
|
@@ -126251,12 +126852,12 @@ import path3 from "node:path";
|
|
|
126251
126852
|
import { loadPlaycademyConfig as loadPlaycademyConfig2 } from "playcademy/utils";
|
|
126252
126853
|
|
|
126253
126854
|
// ../utils/src/uuid.ts
|
|
126254
|
-
var
|
|
126855
|
+
var UUID_REGEX4 = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
126255
126856
|
function isValidUUID2(value) {
|
|
126256
126857
|
if (!value || typeof value !== "string") {
|
|
126257
126858
|
return false;
|
|
126258
126859
|
}
|
|
126259
|
-
return
|
|
126860
|
+
return UUID_REGEX4.test(value);
|
|
126260
126861
|
}
|
|
126261
126862
|
// ../utils/src/ansi.ts
|
|
126262
126863
|
var colors3 = {
|