@playcademy/vite-plugin 1.1.1-beta.2 → 1.1.1-beta.4
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 +1781 -1188
- 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.4",
|
|
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";
|
|
@@ -24449,7 +24450,8 @@ var DEMO_DISPLAY_NAME_PLACEHOLDER = "Demo Player";
|
|
|
24449
24450
|
var init_auth = __esm(() => {
|
|
24450
24451
|
AUTH_PROVIDER_IDS = {
|
|
24451
24452
|
TIMEBACK: "timeback",
|
|
24452
|
-
TIMEBACK_LTI: "timeback-lti"
|
|
24453
|
+
TIMEBACK_LTI: "timeback-lti",
|
|
24454
|
+
PLAYCADEMY: "playcademy"
|
|
24453
24455
|
};
|
|
24454
24456
|
});
|
|
24455
24457
|
var init_typescript = () => {};
|
|
@@ -25342,7 +25344,7 @@ var package_default2;
|
|
|
25342
25344
|
var init_package = __esm(() => {
|
|
25343
25345
|
package_default2 = {
|
|
25344
25346
|
name: "@playcademy/sandbox",
|
|
25345
|
-
version: "0.5.
|
|
25347
|
+
version: "0.5.1",
|
|
25346
25348
|
description: "Local development server for Playcademy game development",
|
|
25347
25349
|
type: "module",
|
|
25348
25350
|
exports: {
|
|
@@ -35608,25 +35610,34 @@ var init_table6 = __esm(() => {
|
|
|
35608
35610
|
})
|
|
35609
35611
|
}));
|
|
35610
35612
|
});
|
|
35613
|
+
var gameTimebackIntegrationStatusEnum;
|
|
35611
35614
|
var gameTimebackIntegrations;
|
|
35612
35615
|
var gameTimebackAssessmentTests;
|
|
35613
35616
|
var gameTimebackMetricDiscrepancyVerifications;
|
|
35614
35617
|
var init_table7 = __esm(() => {
|
|
35618
|
+
init_drizzle_orm();
|
|
35615
35619
|
init_pg_core();
|
|
35616
35620
|
init_table5();
|
|
35617
35621
|
init_table3();
|
|
35622
|
+
gameTimebackIntegrationStatusEnum = pgEnum("game_timeback_integration_status", [
|
|
35623
|
+
"active",
|
|
35624
|
+
"deactivated"
|
|
35625
|
+
]);
|
|
35618
35626
|
gameTimebackIntegrations = pgTable("game_timeback_integrations", {
|
|
35619
35627
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
35620
35628
|
gameId: uuid("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
|
|
35621
35629
|
courseId: text("course_id").notNull(),
|
|
35622
35630
|
grade: integer("grade").notNull(),
|
|
35623
35631
|
subject: text("subject").notNull(),
|
|
35632
|
+
status: gameTimebackIntegrationStatusEnum("status"),
|
|
35624
35633
|
totalXp: integer("total_xp"),
|
|
35625
35634
|
lastVerifiedAt: timestamp("last_verified_at", { withTimezone: true }),
|
|
35635
|
+
deactivatedAt: timestamp("deactivated_at", { withTimezone: true }),
|
|
35636
|
+
reactivatedAt: timestamp("reactivated_at", { withTimezone: true }),
|
|
35626
35637
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
35627
35638
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
35628
35639
|
}, (table3) => [
|
|
35629
|
-
uniqueIndex("game_timeback_integrations_game_grade_subject_idx").on(table3.gameId, table3.grade, table3.subject)
|
|
35640
|
+
uniqueIndex("game_timeback_integrations_game_grade_subject_idx").on(table3.gameId, table3.grade, table3.subject).where(sql`${table3.status} IS DISTINCT FROM 'deactivated'`)
|
|
35630
35641
|
]);
|
|
35631
35642
|
gameTimebackAssessmentTests = pgTable("game_timeback_assessment_tests", {
|
|
35632
35643
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
@@ -35667,6 +35678,7 @@ __export(exports_tables_index, {
|
|
|
35667
35678
|
gameTypeEnum: () => gameTypeEnum,
|
|
35668
35679
|
gameTimebackMetricDiscrepancyVerifications: () => gameTimebackMetricDiscrepancyVerifications,
|
|
35669
35680
|
gameTimebackIntegrations: () => gameTimebackIntegrations,
|
|
35681
|
+
gameTimebackIntegrationStatusEnum: () => gameTimebackIntegrationStatusEnum,
|
|
35670
35682
|
gameTimebackAssessmentTests: () => gameTimebackAssessmentTests,
|
|
35671
35683
|
gameScoresRelations: () => gameScoresRelations,
|
|
35672
35684
|
gameScores: () => gameScores,
|
|
@@ -48677,6 +48689,7 @@ var init_helpers = __esm(() => {
|
|
|
48677
48689
|
init_mime();
|
|
48678
48690
|
});
|
|
48679
48691
|
var init_provider = __esm(() => {
|
|
48692
|
+
init_src();
|
|
48680
48693
|
init_src();
|
|
48681
48694
|
init_core();
|
|
48682
48695
|
init_constants2();
|
|
@@ -51748,21 +51761,626 @@ var init_game_member_service = __esm(() => {
|
|
|
51748
51761
|
init_spans();
|
|
51749
51762
|
init_errors();
|
|
51750
51763
|
});
|
|
51764
|
+
function isActiveGameTimebackIntegrationStatus(statusColumn = gameTimebackIntegrations.status) {
|
|
51765
|
+
return sql`${statusColumn} IS DISTINCT FROM ${DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS}`;
|
|
51766
|
+
}
|
|
51767
|
+
var ACTIVE_GAME_TIMEBACK_INTEGRATION_STATUS = "active";
|
|
51768
|
+
var DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS = "deactivated";
|
|
51769
|
+
var init_helpers2 = __esm(() => {
|
|
51770
|
+
init_drizzle_orm();
|
|
51771
|
+
init_table7();
|
|
51772
|
+
});
|
|
51773
|
+
var init_helpers_index = __esm(() => {
|
|
51774
|
+
init_helpers2();
|
|
51775
|
+
});
|
|
51751
51776
|
function sleep(ms) {
|
|
51752
51777
|
if (ms <= 0) {
|
|
51753
51778
|
return Promise.resolve();
|
|
51754
51779
|
}
|
|
51755
51780
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
51756
51781
|
}
|
|
51782
|
+
function isObject(value) {
|
|
51783
|
+
return typeof value === "object" && value !== null;
|
|
51784
|
+
}
|
|
51785
|
+
function isCourseMetadata(value) {
|
|
51786
|
+
return isObject(value);
|
|
51787
|
+
}
|
|
51788
|
+
function isResourceMetadata(value) {
|
|
51789
|
+
return isObject(value);
|
|
51790
|
+
}
|
|
51791
|
+
function isPlaycademyResourceMetadata(value) {
|
|
51792
|
+
if (!isObject(value)) {
|
|
51793
|
+
return false;
|
|
51794
|
+
}
|
|
51795
|
+
if (!("mastery" in value) || value.mastery === undefined) {
|
|
51796
|
+
return true;
|
|
51797
|
+
}
|
|
51798
|
+
return isObject(value.mastery);
|
|
51799
|
+
}
|
|
51800
|
+
function isTimebackSubject(value) {
|
|
51801
|
+
return typeof value === "string" && SUBJECT_VALUES.includes(value);
|
|
51802
|
+
}
|
|
51803
|
+
function isTimebackGrade(value) {
|
|
51804
|
+
return typeof value === "number" && Number.isInteger(value) && GRADE_VALUES.includes(value);
|
|
51805
|
+
}
|
|
51806
|
+
var __esm2 = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
51807
|
+
var TIMEBACK_API_URLS;
|
|
51808
|
+
var TIMEBACK_AUTH_URLS;
|
|
51809
|
+
var CALIPER_API_URLS;
|
|
51810
|
+
var ONEROSTER_ENDPOINTS;
|
|
51811
|
+
var QTI_ENDPOINTS;
|
|
51812
|
+
var CALIPER_ENDPOINTS;
|
|
51813
|
+
var CALIPER_CONSTANTS;
|
|
51814
|
+
var TIMEBACK_EVENT_TYPES;
|
|
51815
|
+
var TIMEBACK_ACTIONS;
|
|
51816
|
+
var TIMEBACK_TYPES;
|
|
51817
|
+
var ACTIVITY_METRIC_TYPES;
|
|
51818
|
+
var TIME_METRIC_TYPES;
|
|
51819
|
+
var TIMEBACK_SUBJECTS;
|
|
51820
|
+
var TIMEBACK_GRADE_LEVELS;
|
|
51821
|
+
var TIMEBACK_GRADE_LEVEL_LABELS;
|
|
51822
|
+
var CALIPER_SUBJECTS;
|
|
51823
|
+
var ONEROSTER_STATUS;
|
|
51824
|
+
var SCORE_STATUS;
|
|
51825
|
+
var ENV_VARS;
|
|
51826
|
+
var HTTP_DEFAULTS;
|
|
51827
|
+
var AUTH_DEFAULTS;
|
|
51828
|
+
var CACHE_DEFAULTS;
|
|
51829
|
+
var CONFIG_DEFAULTS;
|
|
51830
|
+
var PLAYCADEMY_DEFAULTS;
|
|
51831
|
+
var RESOURCE_DEFAULTS;
|
|
51832
|
+
var HTTP_STATUS;
|
|
51833
|
+
var ERROR_NAMES;
|
|
51834
|
+
var init_constants3;
|
|
51835
|
+
var SUBJECT_VALUES;
|
|
51836
|
+
var GRADE_VALUES;
|
|
51837
|
+
var init_types2 = __esm(() => {
|
|
51838
|
+
init_src();
|
|
51839
|
+
init_constants3 = __esm2(() => {
|
|
51840
|
+
TIMEBACK_API_URLS = {
|
|
51841
|
+
production: "https://api.alpha-1edtech.ai",
|
|
51842
|
+
staging: "https://api.staging.alpha-1edtech.com"
|
|
51843
|
+
};
|
|
51844
|
+
TIMEBACK_AUTH_URLS = {
|
|
51845
|
+
production: "https://prod-beyond-timeback-api-2-idp.auth.us-east-1.amazoncognito.com",
|
|
51846
|
+
staging: "https://alpha-auth-development-idp.auth.us-west-2.amazoncognito.com"
|
|
51847
|
+
};
|
|
51848
|
+
CALIPER_API_URLS = {
|
|
51849
|
+
production: "https://caliper.alpha-1edtech.ai",
|
|
51850
|
+
staging: "https://caliper-staging.alpha-1edtech.com"
|
|
51851
|
+
};
|
|
51852
|
+
ONEROSTER_ENDPOINTS = {
|
|
51853
|
+
organizations: "/ims/oneroster/rostering/v1p2/orgs",
|
|
51854
|
+
courses: "/ims/oneroster/rostering/v1p2/courses",
|
|
51855
|
+
courseComponents: "/ims/oneroster/rostering/v1p2/courses/components",
|
|
51856
|
+
resources: "/ims/oneroster/resources/v1p2/resources",
|
|
51857
|
+
componentResources: "/ims/oneroster/rostering/v1p2/courses/component-resources",
|
|
51858
|
+
classes: "/ims/oneroster/rostering/v1p2/classes",
|
|
51859
|
+
enrollments: "/ims/oneroster/rostering/v1p2/enrollments",
|
|
51860
|
+
assessmentLineItems: "/ims/oneroster/gradebook/v1p2/assessmentLineItems",
|
|
51861
|
+
assessmentResults: "/ims/oneroster/gradebook/v1p2/assessmentResults",
|
|
51862
|
+
users: "/ims/oneroster/rostering/v1p2/users"
|
|
51863
|
+
};
|
|
51864
|
+
QTI_ENDPOINTS = {
|
|
51865
|
+
assessmentTests: "/assessment-tests",
|
|
51866
|
+
assessmentItems: "/assessment-items"
|
|
51867
|
+
};
|
|
51868
|
+
CALIPER_ENDPOINTS = {
|
|
51869
|
+
event: "/caliper/event",
|
|
51870
|
+
events: "/caliper/events",
|
|
51871
|
+
validate: "/caliper/event/validate"
|
|
51872
|
+
};
|
|
51873
|
+
CALIPER_CONSTANTS = {
|
|
51874
|
+
context: "http://purl.imsglobal.org/ctx/caliper/v1p2",
|
|
51875
|
+
profile: "TimebackProfile",
|
|
51876
|
+
dataVersion: "http://purl.imsglobal.org/ctx/caliper/v1p2"
|
|
51877
|
+
};
|
|
51878
|
+
TIMEBACK_EVENT_TYPES = {
|
|
51879
|
+
activityEvent: "ActivityEvent",
|
|
51880
|
+
timeSpentEvent: "TimeSpentEvent"
|
|
51881
|
+
};
|
|
51882
|
+
TIMEBACK_ACTIONS = {
|
|
51883
|
+
completed: "Completed",
|
|
51884
|
+
spentTime: "SpentTime"
|
|
51885
|
+
};
|
|
51886
|
+
TIMEBACK_TYPES = {
|
|
51887
|
+
user: "TimebackUser",
|
|
51888
|
+
activityContext: "TimebackActivityContext",
|
|
51889
|
+
activityMetricsCollection: "TimebackActivityMetricsCollection",
|
|
51890
|
+
timeSpentMetricsCollection: "TimebackTimeSpentMetricsCollection"
|
|
51891
|
+
};
|
|
51892
|
+
ACTIVITY_METRIC_TYPES = {
|
|
51893
|
+
totalQuestions: "totalQuestions",
|
|
51894
|
+
correctQuestions: "correctQuestions",
|
|
51895
|
+
xpEarned: "xpEarned",
|
|
51896
|
+
masteredUnits: "masteredUnits"
|
|
51897
|
+
};
|
|
51898
|
+
TIME_METRIC_TYPES = {
|
|
51899
|
+
active: "active",
|
|
51900
|
+
inactive: "inactive",
|
|
51901
|
+
waste: "waste",
|
|
51902
|
+
unknown: "unknown",
|
|
51903
|
+
antiPattern: "anti-pattern"
|
|
51904
|
+
};
|
|
51905
|
+
TIMEBACK_SUBJECTS = [
|
|
51906
|
+
"Math",
|
|
51907
|
+
"FastMath",
|
|
51908
|
+
"Science",
|
|
51909
|
+
"Social Studies",
|
|
51910
|
+
"Language",
|
|
51911
|
+
"Reading",
|
|
51912
|
+
"Vocabulary",
|
|
51913
|
+
"Writing"
|
|
51914
|
+
];
|
|
51915
|
+
TIMEBACK_GRADE_LEVELS = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
|
|
51916
|
+
TIMEBACK_GRADE_LEVEL_LABELS = {
|
|
51917
|
+
"-1": "pre-k",
|
|
51918
|
+
"0": "kindergarten",
|
|
51919
|
+
"1": "1st grade",
|
|
51920
|
+
"2": "2nd grade",
|
|
51921
|
+
"3": "3rd grade",
|
|
51922
|
+
"4": "4th grade",
|
|
51923
|
+
"5": "5th grade",
|
|
51924
|
+
"6": "6th grade",
|
|
51925
|
+
"7": "7th grade",
|
|
51926
|
+
"8": "8th grade",
|
|
51927
|
+
"9": "9th grade",
|
|
51928
|
+
"10": "10th grade",
|
|
51929
|
+
"11": "11th grade",
|
|
51930
|
+
"12": "12th grade",
|
|
51931
|
+
"13": "AP"
|
|
51932
|
+
};
|
|
51933
|
+
CALIPER_SUBJECTS = {
|
|
51934
|
+
Reading: "Reading",
|
|
51935
|
+
Language: "Language",
|
|
51936
|
+
Vocabulary: "Vocabulary",
|
|
51937
|
+
SocialStudies: "Social Studies",
|
|
51938
|
+
Writing: "Writing",
|
|
51939
|
+
Science: "Science",
|
|
51940
|
+
FastMath: "FastMath",
|
|
51941
|
+
Math: "Math",
|
|
51942
|
+
None: "None"
|
|
51943
|
+
};
|
|
51944
|
+
ONEROSTER_STATUS = {
|
|
51945
|
+
active: "active",
|
|
51946
|
+
toBeDeleted: "tobedeleted"
|
|
51947
|
+
};
|
|
51948
|
+
SCORE_STATUS = {
|
|
51949
|
+
exempt: "exempt",
|
|
51950
|
+
fullyGraded: "fully graded",
|
|
51951
|
+
notSubmitted: "not submitted",
|
|
51952
|
+
partiallyGraded: "partially graded",
|
|
51953
|
+
submitted: "submitted"
|
|
51954
|
+
};
|
|
51955
|
+
ENV_VARS = {
|
|
51956
|
+
clientId: "TIMEBACK_CLIENT_ID",
|
|
51957
|
+
clientSecret: "TIMEBACK_CLIENT_SECRET",
|
|
51958
|
+
baseUrl: "TIMEBACK_BASE_URL",
|
|
51959
|
+
environment: "TIMEBACK_ENVIRONMENT",
|
|
51960
|
+
vendorResourceId: "TIMEBACK_VENDOR_RESOURCE_ID",
|
|
51961
|
+
launchBaseUrl: "GAME_URL",
|
|
51962
|
+
qtiClientId: "QTI_CLIENT_ID",
|
|
51963
|
+
qtiClientSecret: "QTI_CLIENT_SECRET",
|
|
51964
|
+
qtiAuthUrl: "QTI_AUTH_URL"
|
|
51965
|
+
};
|
|
51966
|
+
HTTP_DEFAULTS = {
|
|
51967
|
+
timeout: 30000,
|
|
51968
|
+
retries: 3,
|
|
51969
|
+
retryBackoffBase: 2
|
|
51970
|
+
};
|
|
51971
|
+
AUTH_DEFAULTS = {
|
|
51972
|
+
tokenCacheDuration: 50000
|
|
51973
|
+
};
|
|
51974
|
+
CACHE_DEFAULTS = {
|
|
51975
|
+
defaultTTL: 600000,
|
|
51976
|
+
defaultMaxSize: 500,
|
|
51977
|
+
defaultName: "TimebackCache",
|
|
51978
|
+
studentTTL: 600000,
|
|
51979
|
+
studentMaxSize: 500,
|
|
51980
|
+
assessmentTTL: 1800000,
|
|
51981
|
+
assessmentMaxSize: 200,
|
|
51982
|
+
enrollmentTTL: 5000,
|
|
51983
|
+
enrollmentMaxSize: 100
|
|
51984
|
+
};
|
|
51985
|
+
CONFIG_DEFAULTS = {
|
|
51986
|
+
fileNames: ["timeback.config.js", "timeback.config.json"]
|
|
51987
|
+
};
|
|
51988
|
+
PLAYCADEMY_DEFAULTS = {
|
|
51989
|
+
organization: TIMEBACK_ORG_SOURCED_ID,
|
|
51990
|
+
launchBaseUrls: PLAYCADEMY_BASE_URLS
|
|
51991
|
+
};
|
|
51992
|
+
RESOURCE_DEFAULTS = {
|
|
51993
|
+
organization: {
|
|
51994
|
+
name: TIMEBACK_ORG_NAME,
|
|
51995
|
+
type: TIMEBACK_ORG_TYPE
|
|
51996
|
+
},
|
|
51997
|
+
course: {
|
|
51998
|
+
gradingScheme: TIMEBACK_COURSE_DEFAULTS.gradingScheme,
|
|
51999
|
+
level: TIMEBACK_COURSE_DEFAULTS.level,
|
|
52000
|
+
metadata: {
|
|
52001
|
+
goals: TIMEBACK_COURSE_DEFAULTS.goals,
|
|
52002
|
+
metrics: TIMEBACK_COURSE_DEFAULTS.metrics
|
|
52003
|
+
}
|
|
52004
|
+
},
|
|
52005
|
+
component: TIMEBACK_COMPONENT_DEFAULTS,
|
|
52006
|
+
resource: TIMEBACK_RESOURCE_DEFAULTS,
|
|
52007
|
+
componentResource: TIMEBACK_COMPONENT_RESOURCE_DEFAULTS
|
|
52008
|
+
};
|
|
52009
|
+
HTTP_STATUS = {
|
|
52010
|
+
CLIENT_ERROR_MIN: 400,
|
|
52011
|
+
CLIENT_ERROR_MAX: 500,
|
|
52012
|
+
SERVER_ERROR_MIN: 500
|
|
52013
|
+
};
|
|
52014
|
+
ERROR_NAMES = {
|
|
52015
|
+
timebackAuth: "TimebackAuthError",
|
|
52016
|
+
timebackApi: "TimebackApiError",
|
|
52017
|
+
timebackConfig: "TimebackConfigError",
|
|
52018
|
+
timebackSdk: "TimebackSDKError"
|
|
52019
|
+
};
|
|
52020
|
+
});
|
|
52021
|
+
init_constants3();
|
|
52022
|
+
SUBJECT_VALUES = TIMEBACK_SUBJECTS;
|
|
52023
|
+
GRADE_VALUES = TIMEBACK_GRADE_LEVELS;
|
|
52024
|
+
});
|
|
52025
|
+
function kebabToTitleCase(kebabStr) {
|
|
52026
|
+
return kebabStr.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
52027
|
+
}
|
|
52028
|
+
function isRecord2(value) {
|
|
52029
|
+
return typeof value === "object" && value !== null;
|
|
52030
|
+
}
|
|
52031
|
+
function filterEnrollmentsByGame(enrollments, gameId) {
|
|
52032
|
+
return enrollments.filter((enrollment) => enrollment.gameId === gameId).map(({ gameId: _2, ...enrollment }) => enrollment);
|
|
52033
|
+
}
|
|
52034
|
+
function mapEnrollmentsToUserEnrollments(enrollments, integrations) {
|
|
52035
|
+
const enrollmentByCourse = new Map(enrollments.map((enrollment) => [enrollment.courseId, enrollment]));
|
|
52036
|
+
const courseToSchool = new Map(enrollments.filter((enrollment) => enrollment.school?.id).map((enrollment) => [enrollment.courseId, enrollment.school.id]));
|
|
52037
|
+
return integrations.map((integration) => {
|
|
52038
|
+
const enrollment = enrollmentByCourse.get(integration.courseId);
|
|
52039
|
+
return {
|
|
52040
|
+
gameId: integration.gameId,
|
|
52041
|
+
grade: integration.grade,
|
|
52042
|
+
subject: integration.subject,
|
|
52043
|
+
courseId: integration.courseId,
|
|
52044
|
+
orgId: courseToSchool.get(integration.courseId),
|
|
52045
|
+
...enrollment ? { id: enrollment.sourcedId } : {}
|
|
52046
|
+
};
|
|
52047
|
+
});
|
|
52048
|
+
}
|
|
52049
|
+
function buildGameTimebackSummaries(integrations) {
|
|
52050
|
+
const summaries = {};
|
|
52051
|
+
for (const integration of integrations) {
|
|
52052
|
+
const summary = summaries[integration.gameId] ?? {
|
|
52053
|
+
subject: null,
|
|
52054
|
+
hasActiveIntegration: false,
|
|
52055
|
+
hasRemovedIntegration: false
|
|
52056
|
+
};
|
|
52057
|
+
const isRemoved = integration.status === DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS;
|
|
52058
|
+
if (isRemoved) {
|
|
52059
|
+
summary.hasRemovedIntegration = true;
|
|
52060
|
+
} else {
|
|
52061
|
+
summary.hasActiveIntegration = true;
|
|
52062
|
+
summary.subject ??= integration.subject;
|
|
52063
|
+
}
|
|
52064
|
+
summaries[integration.gameId] = summary;
|
|
52065
|
+
}
|
|
52066
|
+
return summaries;
|
|
52067
|
+
}
|
|
52068
|
+
function getStringValue(value) {
|
|
52069
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
|
52070
|
+
}
|
|
52071
|
+
function parseSourcedIdFromUrl(url) {
|
|
52072
|
+
if (!url) {
|
|
52073
|
+
return;
|
|
52074
|
+
}
|
|
52075
|
+
const trimmed = url.trim().replace(/\/$/, "");
|
|
52076
|
+
if (!trimmed) {
|
|
52077
|
+
return;
|
|
52078
|
+
}
|
|
52079
|
+
const segments = trimmed.split("/");
|
|
52080
|
+
const lastSegment = segments.at(-1);
|
|
52081
|
+
return lastSegment ? decodeURIComponent(lastSegment) : undefined;
|
|
52082
|
+
}
|
|
52083
|
+
function getGeneratedMetricValue(event, type) {
|
|
52084
|
+
const items = event.generated?.items;
|
|
52085
|
+
if (!Array.isArray(items)) {
|
|
52086
|
+
return;
|
|
52087
|
+
}
|
|
52088
|
+
const metric = items.find((item) => item?.type === type);
|
|
52089
|
+
if (!metric) {
|
|
52090
|
+
return;
|
|
52091
|
+
}
|
|
52092
|
+
const value = typeof metric.value === "number" ? metric.value : Number(metric.value);
|
|
52093
|
+
return Number.isFinite(value) ? value : undefined;
|
|
52094
|
+
}
|
|
52095
|
+
function getMergedCaliperExtensions(event) {
|
|
52096
|
+
const objectActivityExtensions = isRecord2(event.object.activity?.extensions) ? event.object.activity.extensions : undefined;
|
|
52097
|
+
const generatedExtensions = isRecord2(event.generated?.extensions) ? event.generated.extensions : undefined;
|
|
52098
|
+
const eventExtensions = isRecord2(event.extensions) ? event.extensions : undefined;
|
|
52099
|
+
return {
|
|
52100
|
+
...objectActivityExtensions,
|
|
52101
|
+
...generatedExtensions,
|
|
52102
|
+
...eventExtensions
|
|
52103
|
+
};
|
|
52104
|
+
}
|
|
52105
|
+
function getPlaycademyMetadata(event) {
|
|
52106
|
+
const extensions = getMergedCaliperExtensions(event);
|
|
52107
|
+
return isRecord2(extensions.playcademy) ? extensions.playcademy : undefined;
|
|
52108
|
+
}
|
|
52109
|
+
function getActivityId(event, playcademy) {
|
|
52110
|
+
const metadataActivityId = getStringValue(playcademy?.activityId);
|
|
52111
|
+
if (metadataActivityId) {
|
|
52112
|
+
return metadataActivityId;
|
|
52113
|
+
}
|
|
52114
|
+
const activityId = getStringValue(event.object.activity?.id);
|
|
52115
|
+
if (activityId) {
|
|
52116
|
+
return activityId;
|
|
52117
|
+
}
|
|
52118
|
+
const objectId = getStringValue(event.object.id);
|
|
52119
|
+
if (!objectId) {
|
|
52120
|
+
return;
|
|
52121
|
+
}
|
|
52122
|
+
const trimmed = objectId.replace(/\/$/, "");
|
|
52123
|
+
const segments = trimmed.split("/");
|
|
52124
|
+
const activityIndex = segments.lastIndexOf("activities");
|
|
52125
|
+
if (activityIndex !== -1 && segments.length >= activityIndex + 3) {
|
|
52126
|
+
const candidate = segments[activityIndex + 2];
|
|
52127
|
+
return candidate ? decodeURIComponent(candidate) : undefined;
|
|
52128
|
+
}
|
|
52129
|
+
return;
|
|
52130
|
+
}
|
|
52131
|
+
function buildResourceMetadata({
|
|
52132
|
+
baseMetadata,
|
|
52133
|
+
subject,
|
|
52134
|
+
grade,
|
|
52135
|
+
totalXp,
|
|
52136
|
+
masterableUnits
|
|
52137
|
+
}) {
|
|
52138
|
+
const normalizedBaseMetadata = isResourceMetadata(baseMetadata) ? baseMetadata : undefined;
|
|
52139
|
+
const metadata2 = {
|
|
52140
|
+
...normalizedBaseMetadata
|
|
52141
|
+
};
|
|
52142
|
+
metadata2.subject = subject;
|
|
52143
|
+
metadata2.grades = [grade];
|
|
52144
|
+
metadata2.xp = totalXp;
|
|
52145
|
+
if (masterableUnits !== undefined && masterableUnits !== null) {
|
|
52146
|
+
const existingPlaycademy = isPlaycademyResourceMetadata(metadata2.playcademy) ? metadata2.playcademy : undefined;
|
|
52147
|
+
metadata2.playcademy = {
|
|
52148
|
+
...existingPlaycademy,
|
|
52149
|
+
mastery: {
|
|
52150
|
+
...existingPlaycademy?.mastery,
|
|
52151
|
+
masterableUnits
|
|
52152
|
+
}
|
|
52153
|
+
};
|
|
52154
|
+
}
|
|
52155
|
+
return metadata2;
|
|
52156
|
+
}
|
|
52157
|
+
function getDurationSecondsFromExtensions(event) {
|
|
52158
|
+
const extensions = getMergedCaliperExtensions(event);
|
|
52159
|
+
const playcademy = isRecord2(extensions.playcademy) ? extensions.playcademy : undefined;
|
|
52160
|
+
const rawValue = extensions.durationSeconds ?? playcademy?.durationSeconds;
|
|
52161
|
+
const value = typeof rawValue === "number" ? rawValue : Number(rawValue);
|
|
52162
|
+
return Number.isFinite(value) ? value : undefined;
|
|
52163
|
+
}
|
|
52164
|
+
function getCanonicalRunId(session2) {
|
|
52165
|
+
const sessionId = getStringValue(session2?.id);
|
|
52166
|
+
if (!sessionId) {
|
|
52167
|
+
return;
|
|
52168
|
+
}
|
|
52169
|
+
return sessionId.replace(/^urn:uuid:/, "");
|
|
52170
|
+
}
|
|
52171
|
+
function getResumeId(event) {
|
|
52172
|
+
const playcademy = getPlaycademyMetadata(event);
|
|
52173
|
+
return getStringValue(playcademy?.resumeId);
|
|
52174
|
+
}
|
|
52175
|
+
function isCaliperRemediationOrCompletionEvent(event) {
|
|
52176
|
+
const playcademy = getPlaycademyMetadata(event);
|
|
52177
|
+
return REMEDIATION_OR_COMPLETION_EVENT_KINDS.has(getStringValue(playcademy?.eventKind) || "");
|
|
52178
|
+
}
|
|
52179
|
+
function groupCaliperEventsByRun(events) {
|
|
52180
|
+
const groups = new Map;
|
|
52181
|
+
for (const event of events) {
|
|
52182
|
+
const objectId = getStringValue(event.object.id) || "unknown-activity";
|
|
52183
|
+
const groupKey = `${objectId}::${getStringValue(event.session?.id) || event.externalId}`;
|
|
52184
|
+
const existing = groups.get(groupKey);
|
|
52185
|
+
if (existing) {
|
|
52186
|
+
existing.push(event);
|
|
52187
|
+
} else {
|
|
52188
|
+
groups.set(groupKey, [event]);
|
|
52189
|
+
}
|
|
52190
|
+
}
|
|
52191
|
+
return groups;
|
|
52192
|
+
}
|
|
52193
|
+
function findCaliperEventGroupContainingExternalId(events, externalId) {
|
|
52194
|
+
const targetExternalId = externalId.trim();
|
|
52195
|
+
if (!targetExternalId) {
|
|
52196
|
+
return;
|
|
52197
|
+
}
|
|
52198
|
+
return [...groupCaliperEventsByRun(events).values()].find((group) => group.some((event) => event.externalId === targetExternalId));
|
|
52199
|
+
}
|
|
52200
|
+
function mapCaliperEventGroupToActivity(events, relevantCourseIds) {
|
|
52201
|
+
if (events.length === 0) {
|
|
52202
|
+
return null;
|
|
52203
|
+
}
|
|
52204
|
+
const sortedEvents = events.toSorted((a, b) => a.eventTime.localeCompare(b.eventTime));
|
|
52205
|
+
const activityEvent = [...sortedEvents].toReversed().find((event) => event.type === "ActivityEvent");
|
|
52206
|
+
const contextSource = activityEvent || sortedEvents.at(-1);
|
|
52207
|
+
if (!contextSource) {
|
|
52208
|
+
return null;
|
|
52209
|
+
}
|
|
52210
|
+
const ctx = parseCaliperEventContext(contextSource, relevantCourseIds);
|
|
52211
|
+
if (!ctx) {
|
|
52212
|
+
return null;
|
|
52213
|
+
}
|
|
52214
|
+
const score = activityEvent !== undefined ? (() => {
|
|
52215
|
+
const totalQuestions = getGeneratedMetricValue(activityEvent, "totalQuestions");
|
|
52216
|
+
const correctQuestions = getGeneratedMetricValue(activityEvent, "correctQuestions");
|
|
52217
|
+
if (totalQuestions === undefined || correctQuestions === undefined || totalQuestions <= 0) {
|
|
52218
|
+
return;
|
|
52219
|
+
}
|
|
52220
|
+
return correctQuestions / totalQuestions * 100;
|
|
52221
|
+
})() : undefined;
|
|
52222
|
+
const xpEarned = activityEvent !== undefined ? getGeneratedMetricValue(activityEvent, "xpEarned") : undefined;
|
|
52223
|
+
const masteredUnits = activityEvent !== undefined ? getGeneratedMetricValue(activityEvent, "masteredUnits") : undefined;
|
|
52224
|
+
const timeSpentEvents = sortedEvents.filter((event) => event.type === "TimeSpentEvent");
|
|
52225
|
+
let totalActiveTimeSeconds;
|
|
52226
|
+
if (timeSpentEvents.length > 0) {
|
|
52227
|
+
totalActiveTimeSeconds = timeSpentEvents.reduce((sum, event) => sum + (getGeneratedMetricValue(event, "active") ?? 0), 0);
|
|
52228
|
+
} else if (activityEvent !== undefined) {
|
|
52229
|
+
totalActiveTimeSeconds = getDurationSecondsFromExtensions(activityEvent);
|
|
52230
|
+
}
|
|
52231
|
+
const fallbackActivityId = getActivityId(contextSource, getPlaycademyMetadata(contextSource));
|
|
52232
|
+
const occurredAt = getStringValue(activityEvent?.eventTime) || getStringValue(sortedEvents.at(-1)?.eventTime);
|
|
52233
|
+
const runId = getCanonicalRunId(contextSource.session);
|
|
52234
|
+
const resumeIds = new Set(sortedEvents.map((event) => getResumeId(event)).filter((resumeId) => resumeId !== undefined));
|
|
52235
|
+
const sessionCount = resumeIds.size > 0 ? resumeIds.size : 1;
|
|
52236
|
+
const kind = activityEvent !== undefined ? "activity" : "activity-in-progress";
|
|
52237
|
+
if (!occurredAt) {
|
|
52238
|
+
return null;
|
|
52239
|
+
}
|
|
52240
|
+
return {
|
|
52241
|
+
id: activityEvent?.externalId || sortedEvents.at(-1)?.externalId || events[0].externalId,
|
|
52242
|
+
kind,
|
|
52243
|
+
occurredAt,
|
|
52244
|
+
courseId: ctx.courseId,
|
|
52245
|
+
title: getStringValue(activityEvent?.object.activity?.name) || ctx.titleFromEvent || (fallbackActivityId ? kebabToTitleCase(fallbackActivityId) : "Activity completed"),
|
|
52246
|
+
...ctx.activityId ? { activityId: ctx.activityId } : {},
|
|
52247
|
+
...ctx.appName ? { appName: ctx.appName } : {},
|
|
52248
|
+
...score !== undefined ? { score } : {},
|
|
52249
|
+
...xpEarned !== undefined ? { xpDelta: xpEarned } : {},
|
|
52250
|
+
...masteredUnits !== undefined ? { masteredUnitsDelta: masteredUnits } : {},
|
|
52251
|
+
...totalActiveTimeSeconds !== undefined ? { timeDeltaSeconds: totalActiveTimeSeconds } : {},
|
|
52252
|
+
...runId ? { runId } : {},
|
|
52253
|
+
...sessionCount > 0 ? { sessionCount } : {}
|
|
52254
|
+
};
|
|
52255
|
+
}
|
|
52256
|
+
function parseCaliperEventContext(event, relevantCourseIds) {
|
|
52257
|
+
const playcademy = getPlaycademyMetadata(event);
|
|
52258
|
+
const courseId = getStringValue(playcademy?.courseId) || parseSourcedIdFromUrl(event.object.course?.id);
|
|
52259
|
+
if (!courseId || !relevantCourseIds.has(courseId)) {
|
|
52260
|
+
return null;
|
|
52261
|
+
}
|
|
52262
|
+
const occurredAt = getStringValue(event.eventTime);
|
|
52263
|
+
if (!occurredAt) {
|
|
52264
|
+
return null;
|
|
52265
|
+
}
|
|
52266
|
+
return {
|
|
52267
|
+
courseId,
|
|
52268
|
+
occurredAt,
|
|
52269
|
+
eventKind: getStringValue(playcademy?.eventKind),
|
|
52270
|
+
source: getStringValue(playcademy?.source),
|
|
52271
|
+
reason: getStringValue(playcademy?.reason),
|
|
52272
|
+
titleFromEvent: getStringValue(event.object.activity?.name),
|
|
52273
|
+
appName: getStringValue(event.object.app?.name),
|
|
52274
|
+
activityId: getActivityId(event, playcademy)
|
|
52275
|
+
};
|
|
52276
|
+
}
|
|
52277
|
+
function mapTimeSpentRemediation(event, ctx) {
|
|
52278
|
+
if (ctx.eventKind !== "remediation-time") {
|
|
52279
|
+
return null;
|
|
52280
|
+
}
|
|
52281
|
+
return {
|
|
52282
|
+
id: event.externalId,
|
|
52283
|
+
kind: "remediation-time",
|
|
52284
|
+
occurredAt: ctx.occurredAt,
|
|
52285
|
+
courseId: ctx.courseId,
|
|
52286
|
+
title: "Time Adjustment",
|
|
52287
|
+
activityId: ctx.activityId,
|
|
52288
|
+
appName: ctx.appName,
|
|
52289
|
+
reason: ctx.reason,
|
|
52290
|
+
timeDeltaSeconds: getGeneratedMetricValue(event, "active")
|
|
52291
|
+
};
|
|
52292
|
+
}
|
|
52293
|
+
function mapActivityRemediation(event, ctx) {
|
|
52294
|
+
if (ctx.eventKind === "remediation-xp") {
|
|
52295
|
+
return {
|
|
52296
|
+
id: event.externalId,
|
|
52297
|
+
kind: "remediation-xp",
|
|
52298
|
+
occurredAt: ctx.occurredAt,
|
|
52299
|
+
courseId: ctx.courseId,
|
|
52300
|
+
title: "XP Adjustment",
|
|
52301
|
+
activityId: ctx.activityId,
|
|
52302
|
+
appName: ctx.appName,
|
|
52303
|
+
reason: ctx.reason,
|
|
52304
|
+
xpDelta: getGeneratedMetricValue(event, "xpEarned"),
|
|
52305
|
+
masteredUnitsDelta: getGeneratedMetricValue(event, "masteredUnits")
|
|
52306
|
+
};
|
|
52307
|
+
}
|
|
52308
|
+
if (ctx.eventKind === "remediation-mastery") {
|
|
52309
|
+
return {
|
|
52310
|
+
id: event.externalId,
|
|
52311
|
+
kind: "remediation-mastery",
|
|
52312
|
+
occurredAt: ctx.occurredAt,
|
|
52313
|
+
courseId: ctx.courseId,
|
|
52314
|
+
title: "Mastery Adjustment",
|
|
52315
|
+
activityId: ctx.activityId,
|
|
52316
|
+
appName: ctx.appName,
|
|
52317
|
+
reason: ctx.reason,
|
|
52318
|
+
xpDelta: getGeneratedMetricValue(event, "xpEarned"),
|
|
52319
|
+
masteredUnitsDelta: getGeneratedMetricValue(event, "masteredUnits")
|
|
52320
|
+
};
|
|
52321
|
+
}
|
|
52322
|
+
if (ctx.eventKind === "course-completed") {
|
|
52323
|
+
return {
|
|
52324
|
+
id: event.externalId,
|
|
52325
|
+
kind: "course-completed",
|
|
52326
|
+
occurredAt: ctx.occurredAt,
|
|
52327
|
+
courseId: ctx.courseId,
|
|
52328
|
+
title: ctx.source === "admin" ? "Course marked complete" : "Course completed",
|
|
52329
|
+
activityId: ctx.activityId,
|
|
52330
|
+
appName: ctx.appName,
|
|
52331
|
+
reason: ctx.reason
|
|
52332
|
+
};
|
|
52333
|
+
}
|
|
52334
|
+
if (ctx.eventKind === "course-resumed") {
|
|
52335
|
+
return {
|
|
52336
|
+
id: event.externalId,
|
|
52337
|
+
kind: "course-resumed",
|
|
52338
|
+
occurredAt: ctx.occurredAt,
|
|
52339
|
+
courseId: ctx.courseId,
|
|
52340
|
+
title: "Course resumed",
|
|
52341
|
+
activityId: ctx.activityId,
|
|
52342
|
+
appName: ctx.appName,
|
|
52343
|
+
reason: ctx.reason
|
|
52344
|
+
};
|
|
52345
|
+
}
|
|
52346
|
+
return null;
|
|
52347
|
+
}
|
|
52348
|
+
function mapCaliperEventToRemediationActivity(event, relevantCourseIds) {
|
|
52349
|
+
const ctx = parseCaliperEventContext(event, relevantCourseIds);
|
|
52350
|
+
if (!ctx) {
|
|
52351
|
+
return null;
|
|
52352
|
+
}
|
|
52353
|
+
if (event.type === "TimeSpentEvent") {
|
|
52354
|
+
return mapTimeSpentRemediation(event, ctx);
|
|
52355
|
+
}
|
|
52356
|
+
if (event.type === "ActivityEvent") {
|
|
52357
|
+
return mapActivityRemediation(event, ctx);
|
|
52358
|
+
}
|
|
52359
|
+
return null;
|
|
52360
|
+
}
|
|
52361
|
+
var REMEDIATION_OR_COMPLETION_EVENT_KINDS;
|
|
52362
|
+
var init_timeback_util = __esm(() => {
|
|
52363
|
+
init_helpers_index();
|
|
52364
|
+
init_types2();
|
|
52365
|
+
REMEDIATION_OR_COMPLETION_EVENT_KINDS = new Set([
|
|
52366
|
+
"remediation-xp",
|
|
52367
|
+
"remediation-time",
|
|
52368
|
+
"remediation-mastery",
|
|
52369
|
+
"course-completed",
|
|
52370
|
+
"course-resumed"
|
|
52371
|
+
]);
|
|
52372
|
+
});
|
|
51757
52373
|
var inFlightManifestFetches;
|
|
51758
52374
|
var GameService;
|
|
51759
52375
|
var init_game_service = __esm(() => {
|
|
51760
52376
|
init_drizzle_orm();
|
|
51761
52377
|
init_src();
|
|
52378
|
+
init_helpers_index();
|
|
51762
52379
|
init_tables_index();
|
|
51763
52380
|
init_spans();
|
|
51764
52381
|
init_errors();
|
|
51765
52382
|
init_deployment_util();
|
|
52383
|
+
init_timeback_util();
|
|
51766
52384
|
inFlightManifestFetches = new Map;
|
|
51767
52385
|
GameService = class GameService2 {
|
|
51768
52386
|
deps;
|
|
@@ -51906,6 +52524,7 @@ var init_game_service = __esm(() => {
|
|
|
51906
52524
|
const db2 = this.deps.db;
|
|
51907
52525
|
const integrations = await db2.query.gameTimebackIntegrations.findMany({
|
|
51908
52526
|
columns: { gameId: true, subject: true },
|
|
52527
|
+
where: isActiveGameTimebackIntegrationStatus(),
|
|
51909
52528
|
orderBy: [asc(gameTimebackIntegrations.createdAt)]
|
|
51910
52529
|
});
|
|
51911
52530
|
const subjectMap = {};
|
|
@@ -51916,6 +52535,26 @@ var init_game_service = __esm(() => {
|
|
|
51916
52535
|
}
|
|
51917
52536
|
return subjectMap;
|
|
51918
52537
|
}
|
|
52538
|
+
async getTimebackSummaries(user) {
|
|
52539
|
+
const db2 = this.deps.db;
|
|
52540
|
+
const canSeeAllGames = user.role === "admin" || user.role === "teacher";
|
|
52541
|
+
let accessibleGameIds = null;
|
|
52542
|
+
if (!canSeeAllGames) {
|
|
52543
|
+
const accessibleGames = await this.listAccessible(user);
|
|
52544
|
+
accessibleGameIds = new Set(accessibleGames.map((game2) => game2.id));
|
|
52545
|
+
}
|
|
52546
|
+
if (accessibleGameIds?.size === 0) {
|
|
52547
|
+
return {};
|
|
52548
|
+
}
|
|
52549
|
+
const integrations = await db2.query.gameTimebackIntegrations.findMany({
|
|
52550
|
+
columns: { gameId: true, subject: true, status: true },
|
|
52551
|
+
...accessibleGameIds && {
|
|
52552
|
+
where: inArray(gameTimebackIntegrations.gameId, [...accessibleGameIds])
|
|
52553
|
+
},
|
|
52554
|
+
orderBy: [asc(gameTimebackIntegrations.createdAt)]
|
|
52555
|
+
});
|
|
52556
|
+
return buildGameTimebackSummaries(integrations);
|
|
52557
|
+
}
|
|
51919
52558
|
async getById(gameId, caller) {
|
|
51920
52559
|
const db2 = this.deps.db;
|
|
51921
52560
|
const game2 = await db2.query.games.findFirst({
|
|
@@ -52545,7 +53184,7 @@ class DiscordEmbedBuilder {
|
|
|
52545
53184
|
}
|
|
52546
53185
|
var init_client2 = () => {};
|
|
52547
53186
|
var DiscordColors;
|
|
52548
|
-
var
|
|
53187
|
+
var init_types3 = __esm(() => {
|
|
52549
53188
|
DiscordColors = {
|
|
52550
53189
|
DEFAULT: 0,
|
|
52551
53190
|
WHITE: 16777215,
|
|
@@ -52581,7 +53220,7 @@ var init_types2 = __esm(() => {
|
|
|
52581
53220
|
});
|
|
52582
53221
|
var init_discord = __esm(() => {
|
|
52583
53222
|
init_client2();
|
|
52584
|
-
|
|
53223
|
+
init_types3();
|
|
52585
53224
|
});
|
|
52586
53225
|
function truncateField(value, maxLength = DISCORD_FIELD_LIMIT) {
|
|
52587
53226
|
if (value.length <= maxLength) {
|
|
@@ -53702,7 +54341,7 @@ var init_secrets_service = __esm(() => {
|
|
|
53702
54341
|
});
|
|
53703
54342
|
var ASSET_ROUTE_PREFIX = "/api/assets/";
|
|
53704
54343
|
var ROUTES;
|
|
53705
|
-
var
|
|
54344
|
+
var init_constants4 = __esm(() => {
|
|
53706
54345
|
init_src();
|
|
53707
54346
|
ROUTES = {
|
|
53708
54347
|
INDEX: "/api",
|
|
@@ -53728,7 +54367,7 @@ function prefixSecrets(secrets) {
|
|
|
53728
54367
|
}
|
|
53729
54368
|
var init_setup2 = __esm(() => {
|
|
53730
54369
|
init_src();
|
|
53731
|
-
|
|
54370
|
+
init_constants4();
|
|
53732
54371
|
});
|
|
53733
54372
|
|
|
53734
54373
|
class SeedService {
|
|
@@ -54469,11 +55108,11 @@ var init_schemas3 = __esm(() => {
|
|
|
54469
55108
|
metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
|
|
54470
55109
|
});
|
|
54471
55110
|
});
|
|
54472
|
-
function
|
|
55111
|
+
function isTimebackGrade2(value) {
|
|
54473
55112
|
return Number.isInteger(value) && TIMEBACK_GRADES.includes(value);
|
|
54474
55113
|
}
|
|
54475
|
-
function
|
|
54476
|
-
return
|
|
55114
|
+
function isTimebackSubject2(value) {
|
|
55115
|
+
return TIMEBACK_SUBJECTS2.includes(value);
|
|
54477
55116
|
}
|
|
54478
55117
|
function isValidAdminAttributionDate(value) {
|
|
54479
55118
|
const match = value.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
|
@@ -54488,11 +55127,12 @@ function isValidAdminAttributionDate(value) {
|
|
|
54488
55127
|
return date3.getUTCFullYear() === year && date3.getUTCMonth() + 1 === month && date3.getUTCDate() === day;
|
|
54489
55128
|
}
|
|
54490
55129
|
var TIMEBACK_GRADES;
|
|
54491
|
-
var
|
|
55130
|
+
var TIMEBACK_SUBJECTS2;
|
|
54492
55131
|
var TimebackGradeSchema;
|
|
54493
55132
|
var TimebackSubjectSchema;
|
|
54494
55133
|
var CourseGoalsSchema;
|
|
54495
55134
|
var UpdateGameTimebackIntegrationRequestSchema;
|
|
55135
|
+
var CreateGameTimebackIntegrationRequestSchema;
|
|
54496
55136
|
var TimebackActivityDataSchema;
|
|
54497
55137
|
var EndActivityRequestSchema;
|
|
54498
55138
|
var GameRunMetricsSchema;
|
|
@@ -54527,7 +55167,7 @@ var init_schemas4 = __esm(() => {
|
|
|
54527
55167
|
init_esm();
|
|
54528
55168
|
init_table7();
|
|
54529
55169
|
TIMEBACK_GRADES = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
|
|
54530
|
-
|
|
55170
|
+
TIMEBACK_SUBJECTS2 = [
|
|
54531
55171
|
"Reading",
|
|
54532
55172
|
"Language",
|
|
54533
55173
|
"Vocabulary",
|
|
@@ -54541,7 +55181,7 @@ var init_schemas4 = __esm(() => {
|
|
|
54541
55181
|
TimebackGradeSchema = exports_external.number().int().refine((val) => TIMEBACK_GRADES.includes(val), {
|
|
54542
55182
|
message: `Grade must be one of: ${TIMEBACK_GRADES.join(", ")}`
|
|
54543
55183
|
});
|
|
54544
|
-
TimebackSubjectSchema = exports_external.enum(
|
|
55184
|
+
TimebackSubjectSchema = exports_external.enum(TIMEBACK_SUBJECTS2);
|
|
54545
55185
|
CourseGoalsSchema = exports_external.object({
|
|
54546
55186
|
dailyXp: exports_external.number().int().nonnegative().nullable().optional(),
|
|
54547
55187
|
dailyLessons: exports_external.number().int().nonnegative().nullable().optional(),
|
|
@@ -54560,6 +55200,15 @@ var init_schemas4 = __esm(() => {
|
|
|
54560
55200
|
isSupplemental: exports_external.boolean().optional(),
|
|
54561
55201
|
timebackVisible: exports_external.boolean().nullable().optional()
|
|
54562
55202
|
});
|
|
55203
|
+
CreateGameTimebackIntegrationRequestSchema = exports_external.object({
|
|
55204
|
+
title: exports_external.string().trim().min(1),
|
|
55205
|
+
courseCode: exports_external.string().trim().min(1),
|
|
55206
|
+
subject: TimebackSubjectSchema,
|
|
55207
|
+
grade: TimebackGradeSchema,
|
|
55208
|
+
totalXp: exports_external.number().int().positive(),
|
|
55209
|
+
masterableUnits: exports_external.number().int().nonnegative(),
|
|
55210
|
+
level: exports_external.string().trim().min(1).optional()
|
|
55211
|
+
});
|
|
54563
55212
|
TimebackActivityDataSchema = exports_external.object({
|
|
54564
55213
|
activityId: exports_external.string().min(1),
|
|
54565
55214
|
activityName: exports_external.string().optional(),
|
|
@@ -55001,9 +55650,6 @@ var init_log = __esm(() => {
|
|
|
55001
55650
|
init_ansi();
|
|
55002
55651
|
init_spinner();
|
|
55003
55652
|
});
|
|
55004
|
-
function kebabToTitleCase(kebabStr) {
|
|
55005
|
-
return kebabStr.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
55006
|
-
}
|
|
55007
55653
|
function formatDateYMDInTimezone(timeZone, date3 = new Date) {
|
|
55008
55654
|
const parts2 = new Intl.DateTimeFormat("en-US", {
|
|
55009
55655
|
timeZone,
|
|
@@ -55106,14 +55752,42 @@ var init_src4 = __esm(() => {
|
|
|
55106
55752
|
init_pure();
|
|
55107
55753
|
});
|
|
55108
55754
|
function createOneRosterUrls(baseUrl) {
|
|
55109
|
-
const effective = baseUrl ||
|
|
55755
|
+
const effective = baseUrl || TIMEBACK_API_URLS2.production;
|
|
55110
55756
|
const base = effective.replace(/\/$/, "");
|
|
55111
55757
|
return {
|
|
55112
|
-
user: (userId) => `${base}${
|
|
55113
|
-
course: (courseId) => `${base}${
|
|
55114
|
-
componentResource: (resourceId) => `${base}${
|
|
55758
|
+
user: (userId) => `${base}${ONEROSTER_ENDPOINTS2.users}/${userId}`,
|
|
55759
|
+
course: (courseId) => `${base}${ONEROSTER_ENDPOINTS2.courses}/${courseId}`,
|
|
55760
|
+
componentResource: (resourceId) => `${base}${ONEROSTER_ENDPOINTS2.componentResources}/${resourceId}`
|
|
55115
55761
|
};
|
|
55116
55762
|
}
|
|
55763
|
+
function resolveCourseIdFromCourseUrl(url2) {
|
|
55764
|
+
try {
|
|
55765
|
+
const parts2 = new URL(url2).pathname.split("/").filter(Boolean);
|
|
55766
|
+
const maybeCourses = parts2.at(-2);
|
|
55767
|
+
const maybeId = parts2.at(-1);
|
|
55768
|
+
if (maybeCourses !== "courses" || !maybeId) {
|
|
55769
|
+
return null;
|
|
55770
|
+
}
|
|
55771
|
+
if (UUID_REGEX2.test(maybeId)) {
|
|
55772
|
+
return maybeId;
|
|
55773
|
+
}
|
|
55774
|
+
if (maybeId.length > 2 && !maybeId.includes("/")) {
|
|
55775
|
+
return maybeId;
|
|
55776
|
+
}
|
|
55777
|
+
return null;
|
|
55778
|
+
} catch {
|
|
55779
|
+
return null;
|
|
55780
|
+
}
|
|
55781
|
+
}
|
|
55782
|
+
function computeCaliperLineItemId(objectId, courseSourcedId, courseUrl) {
|
|
55783
|
+
const recovered = resolveCourseIdFromCourseUrl(courseUrl);
|
|
55784
|
+
if (recovered !== courseSourcedId) {
|
|
55785
|
+
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.`);
|
|
55786
|
+
}
|
|
55787
|
+
const idParts = [objectId, courseSourcedId].join("_");
|
|
55788
|
+
const hashedId = createHash("sha256").update(idParts).digest("hex");
|
|
55789
|
+
return `caliper_${hashedId}`;
|
|
55790
|
+
}
|
|
55117
55791
|
function deriveSourcedIds(courseId) {
|
|
55118
55792
|
return {
|
|
55119
55793
|
course: courseId,
|
|
@@ -55125,7 +55799,7 @@ function deriveSourcedIds(courseId) {
|
|
|
55125
55799
|
async function fetchTimebackConfig(client, courseId) {
|
|
55126
55800
|
const sourcedIds = deriveSourcedIds(courseId);
|
|
55127
55801
|
const [org, course, component, resource, componentResource] = await Promise.all([
|
|
55128
|
-
client.oneroster.organizations.get(
|
|
55802
|
+
client.oneroster.organizations.get(PLAYCADEMY_DEFAULTS2.organization),
|
|
55129
55803
|
client.oneroster.courses.get(sourcedIds.course),
|
|
55130
55804
|
client.oneroster.courseComponents.get(sourcedIds.component),
|
|
55131
55805
|
client.oneroster.resources.get(sourcedIds.resource),
|
|
@@ -55135,7 +55809,7 @@ async function fetchTimebackConfig(client, courseId) {
|
|
|
55135
55809
|
organization: {
|
|
55136
55810
|
name: org.name,
|
|
55137
55811
|
type: org.type,
|
|
55138
|
-
identifier: org.identifier ||
|
|
55812
|
+
identifier: org.identifier || PLAYCADEMY_DEFAULTS2.organization
|
|
55139
55813
|
},
|
|
55140
55814
|
course: {
|
|
55141
55815
|
title: course.title || "",
|
|
@@ -55195,23 +55869,23 @@ async function verifyTimebackResources(client, courseId) {
|
|
|
55195
55869
|
}
|
|
55196
55870
|
};
|
|
55197
55871
|
}
|
|
55198
|
-
function
|
|
55872
|
+
function isObject2(value) {
|
|
55199
55873
|
return typeof value === "object" && value !== null;
|
|
55200
55874
|
}
|
|
55201
|
-
function
|
|
55202
|
-
if (!
|
|
55875
|
+
function isPlaycademyResourceMetadata2(value) {
|
|
55876
|
+
if (!isObject2(value)) {
|
|
55203
55877
|
return false;
|
|
55204
55878
|
}
|
|
55205
55879
|
if (!("mastery" in value) || value.mastery === undefined) {
|
|
55206
55880
|
return true;
|
|
55207
55881
|
}
|
|
55208
|
-
return
|
|
55882
|
+
return isObject2(value.mastery);
|
|
55209
55883
|
}
|
|
55210
|
-
function
|
|
55211
|
-
return typeof value === "string" &&
|
|
55884
|
+
function isTimebackSubject3(value) {
|
|
55885
|
+
return typeof value === "string" && SUBJECT_VALUES2.includes(value);
|
|
55212
55886
|
}
|
|
55213
|
-
function
|
|
55214
|
-
return typeof value === "number" && Number.isInteger(value) &&
|
|
55887
|
+
function isTimebackGrade3(value) {
|
|
55888
|
+
return typeof value === "number" && Number.isInteger(value) && GRADE_VALUES2.includes(value);
|
|
55215
55889
|
}
|
|
55216
55890
|
async function deleteTimebackResources(client, courseId) {
|
|
55217
55891
|
const sourcedIds = deriveSourcedIds(courseId);
|
|
@@ -55239,17 +55913,7 @@ async function deleteTimebackResources(client, courseId) {
|
|
|
55239
55913
|
prerequisites: config2.component.prerequisites,
|
|
55240
55914
|
prerequisiteCriteria: config2.component.prerequisiteCriteria
|
|
55241
55915
|
}),
|
|
55242
|
-
client.oneroster.resources.
|
|
55243
|
-
sourcedId: sourcedIds.resource,
|
|
55244
|
-
status: "tobedeleted",
|
|
55245
|
-
title: config2.resource.title,
|
|
55246
|
-
vendorResourceId: config2.resource.vendorResourceId,
|
|
55247
|
-
vendorId: config2.resource.vendorId,
|
|
55248
|
-
applicationId: config2.resource.applicationId,
|
|
55249
|
-
roles: config2.resource.roles,
|
|
55250
|
-
importance: config2.resource.importance,
|
|
55251
|
-
metadata: config2.resource.metadata
|
|
55252
|
-
}),
|
|
55916
|
+
client.oneroster.resources.delete(sourcedIds.resource),
|
|
55253
55917
|
client.oneroster.componentResources.update(sourcedIds.componentResource, {
|
|
55254
55918
|
sourcedId: sourcedIds.componentResource,
|
|
55255
55919
|
status: "tobedeleted",
|
|
@@ -55262,6 +55926,15 @@ async function deleteTimebackResources(client, courseId) {
|
|
|
55262
55926
|
]);
|
|
55263
55927
|
setAttribute("app.timeback.resources_deleted", true);
|
|
55264
55928
|
}
|
|
55929
|
+
async function updateTimebackCourseStatus(client, courseId, status) {
|
|
55930
|
+
const course = await client.oneroster.courses.get(courseId);
|
|
55931
|
+
await client.oneroster.courses.update(courseId, {
|
|
55932
|
+
...course,
|
|
55933
|
+
sourcedId: courseId,
|
|
55934
|
+
status
|
|
55935
|
+
});
|
|
55936
|
+
setAttribute("app.timeback.course_status", status);
|
|
55937
|
+
}
|
|
55265
55938
|
async function createCourse(client, config2) {
|
|
55266
55939
|
const courseData = {
|
|
55267
55940
|
status: "active",
|
|
@@ -55510,14 +56183,14 @@ async function getTimebackTokenResponse(config2) {
|
|
|
55510
56183
|
}
|
|
55511
56184
|
}
|
|
55512
56185
|
function getAuthUrl(environment = "production") {
|
|
55513
|
-
return
|
|
56186
|
+
return TIMEBACK_AUTH_URLS2[environment];
|
|
55514
56187
|
}
|
|
55515
56188
|
function parseEduBridgeGrade(value) {
|
|
55516
56189
|
if (value === null || value === undefined || value.trim() === "") {
|
|
55517
56190
|
return null;
|
|
55518
56191
|
}
|
|
55519
56192
|
const parsed = Number(value);
|
|
55520
|
-
return
|
|
56193
|
+
return isTimebackGrade3(parsed) ? parsed : null;
|
|
55521
56194
|
}
|
|
55522
56195
|
function normalizeHighestGradeMastered(response, subject) {
|
|
55523
56196
|
const grades = {
|
|
@@ -55594,12 +56267,12 @@ async function withTimebackClientTelemetry(fn) {
|
|
|
55594
56267
|
}
|
|
55595
56268
|
function handleHttpError(res, errorBody, attempt, retries, context2) {
|
|
55596
56269
|
const error = new TimebackApiError(res.status, res.statusText, errorBody);
|
|
55597
|
-
if (res.status >=
|
|
56270
|
+
if (res.status >= HTTP_STATUS2.CLIENT_ERROR_MIN && res.status < HTTP_STATUS2.CLIENT_ERROR_MAX) {
|
|
55598
56271
|
recordTimebackHttpFailure();
|
|
55599
56272
|
throw error;
|
|
55600
56273
|
}
|
|
55601
56274
|
if (attempt < retries) {
|
|
55602
|
-
const delay =
|
|
56275
|
+
const delay = HTTP_DEFAULTS2.retryBackoffBase ** attempt * 1000;
|
|
55603
56276
|
recordTimebackRetry();
|
|
55604
56277
|
addEvent("timeback.request_retry", {
|
|
55605
56278
|
"app.timeback.attempt": attempt + 1,
|
|
@@ -55676,7 +56349,7 @@ async function request({
|
|
|
55676
56349
|
const result = handleHttpError(res, errorBody, attempt, retries, { method, path: path2 });
|
|
55677
56350
|
lastError = result.error;
|
|
55678
56351
|
if (result.retry) {
|
|
55679
|
-
const delay =
|
|
56352
|
+
const delay = HTTP_DEFAULTS2.retryBackoffBase ** attempt * 1000;
|
|
55680
56353
|
await new Promise((resolve2) => setTimeout(resolve2, delay));
|
|
55681
56354
|
}
|
|
55682
56355
|
} else {
|
|
@@ -55688,7 +56361,7 @@ async function request({
|
|
|
55688
56361
|
}
|
|
55689
56362
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
55690
56363
|
if (attempt < retries) {
|
|
55691
|
-
const delay =
|
|
56364
|
+
const delay = HTTP_DEFAULTS2.retryBackoffBase ** attempt * 1000;
|
|
55692
56365
|
recordTimebackRetry();
|
|
55693
56366
|
addEvent("timeback.network_retry", {
|
|
55694
56367
|
"app.timeback.attempt": attempt + 1,
|
|
@@ -55737,10 +56410,10 @@ function createCaliperNamespace(client) {
|
|
|
55737
56410
|
const envelope = {
|
|
55738
56411
|
sensor: sensorUrl,
|
|
55739
56412
|
sendTime: new Date().toISOString(),
|
|
55740
|
-
dataVersion:
|
|
56413
|
+
dataVersion: CALIPER_CONSTANTS2.dataVersion,
|
|
55741
56414
|
data: [event]
|
|
55742
56415
|
};
|
|
55743
|
-
return client["requestCaliper"](
|
|
56416
|
+
return client["requestCaliper"](CALIPER_ENDPOINTS2.event, "POST", envelope);
|
|
55744
56417
|
},
|
|
55745
56418
|
emitBatch: async (events, sensorUrl) => {
|
|
55746
56419
|
if (events.length === 0) {
|
|
@@ -55749,10 +56422,10 @@ function createCaliperNamespace(client) {
|
|
|
55749
56422
|
const envelope = {
|
|
55750
56423
|
sensor: sensorUrl,
|
|
55751
56424
|
sendTime: new Date().toISOString(),
|
|
55752
|
-
dataVersion:
|
|
56425
|
+
dataVersion: CALIPER_CONSTANTS2.dataVersion,
|
|
55753
56426
|
data: events
|
|
55754
56427
|
};
|
|
55755
|
-
return client["requestCaliper"](
|
|
56428
|
+
return client["requestCaliper"](CALIPER_ENDPOINTS2.event, "POST", envelope);
|
|
55756
56429
|
},
|
|
55757
56430
|
events: {
|
|
55758
56431
|
list: async (params = {}) => {
|
|
@@ -55782,7 +56455,7 @@ function createCaliperNamespace(client) {
|
|
|
55782
56455
|
query.set(`extensions.${key}`, value);
|
|
55783
56456
|
}
|
|
55784
56457
|
}
|
|
55785
|
-
const requestPath = `${
|
|
56458
|
+
const requestPath = `${CALIPER_ENDPOINTS2.events}?${query.toString()}`;
|
|
55786
56459
|
return client["requestCaliper"](requestPath, "GET");
|
|
55787
56460
|
}
|
|
55788
56461
|
},
|
|
@@ -55792,21 +56465,21 @@ function createCaliperNamespace(client) {
|
|
|
55792
56465
|
gameId: data.gameId
|
|
55793
56466
|
});
|
|
55794
56467
|
const event = {
|
|
55795
|
-
"@context":
|
|
56468
|
+
"@context": CALIPER_CONSTANTS2.context,
|
|
55796
56469
|
id: `urn:uuid:${crypto.randomUUID()}`,
|
|
55797
|
-
type:
|
|
56470
|
+
type: TIMEBACK_EVENT_TYPES2.activityEvent,
|
|
55798
56471
|
eventTime: data.eventTime || new Date().toISOString(),
|
|
55799
|
-
profile:
|
|
56472
|
+
profile: CALIPER_CONSTANTS2.profile,
|
|
55800
56473
|
actor: {
|
|
55801
56474
|
id: urls.user(data.studentId),
|
|
55802
|
-
type:
|
|
56475
|
+
type: TIMEBACK_TYPES2.user,
|
|
55803
56476
|
email: data.studentEmail
|
|
55804
56477
|
},
|
|
55805
|
-
action:
|
|
56478
|
+
action: TIMEBACK_ACTIONS2.completed,
|
|
55806
56479
|
...data.runId ? { session: `urn:uuid:${data.runId}` } : {},
|
|
55807
56480
|
object: {
|
|
55808
56481
|
id: data.objectId || caliper.buildActivityUrl(data),
|
|
55809
|
-
type:
|
|
56482
|
+
type: TIMEBACK_TYPES2.activityContext,
|
|
55810
56483
|
subject: data.subject,
|
|
55811
56484
|
app: {
|
|
55812
56485
|
name: data.appName
|
|
@@ -55820,25 +56493,25 @@ function createCaliperNamespace(client) {
|
|
|
55820
56493
|
},
|
|
55821
56494
|
generated: {
|
|
55822
56495
|
id: data.generatedId || `urn:timeback:metrics:activity-completion-${crypto.randomUUID()}`,
|
|
55823
|
-
type:
|
|
56496
|
+
type: TIMEBACK_TYPES2.activityMetricsCollection,
|
|
55824
56497
|
...data.includeAttempt === false ? {} : { attempt: data.attemptNumber || 1 },
|
|
55825
56498
|
items: [
|
|
55826
56499
|
...data.totalQuestions !== undefined ? [
|
|
55827
56500
|
{
|
|
55828
|
-
type:
|
|
56501
|
+
type: ACTIVITY_METRIC_TYPES2.totalQuestions,
|
|
55829
56502
|
value: data.totalQuestions
|
|
55830
56503
|
}
|
|
55831
56504
|
] : [],
|
|
55832
56505
|
...data.correctQuestions !== undefined ? [
|
|
55833
56506
|
{
|
|
55834
|
-
type:
|
|
56507
|
+
type: ACTIVITY_METRIC_TYPES2.correctQuestions,
|
|
55835
56508
|
value: data.correctQuestions
|
|
55836
56509
|
}
|
|
55837
56510
|
] : [],
|
|
55838
|
-
...data.xpEarned !== undefined ? [{ type:
|
|
56511
|
+
...data.xpEarned !== undefined ? [{ type: ACTIVITY_METRIC_TYPES2.xpEarned, value: data.xpEarned }] : [],
|
|
55839
56512
|
...data.masteredUnits !== undefined ? [
|
|
55840
56513
|
{
|
|
55841
|
-
type:
|
|
56514
|
+
type: ACTIVITY_METRIC_TYPES2.masteredUnits,
|
|
55842
56515
|
value: data.masteredUnits
|
|
55843
56516
|
}
|
|
55844
56517
|
] : []
|
|
@@ -55860,21 +56533,21 @@ function createCaliperNamespace(client) {
|
|
|
55860
56533
|
gameId: data.gameId
|
|
55861
56534
|
});
|
|
55862
56535
|
const event = {
|
|
55863
|
-
"@context":
|
|
56536
|
+
"@context": CALIPER_CONSTANTS2.context,
|
|
55864
56537
|
id: `urn:uuid:${crypto.randomUUID()}`,
|
|
55865
|
-
type:
|
|
56538
|
+
type: TIMEBACK_EVENT_TYPES2.timeSpentEvent,
|
|
55866
56539
|
eventTime: data.eventTime || new Date().toISOString(),
|
|
55867
|
-
profile:
|
|
56540
|
+
profile: CALIPER_CONSTANTS2.profile,
|
|
55868
56541
|
actor: {
|
|
55869
56542
|
id: urls.user(data.studentId),
|
|
55870
|
-
type:
|
|
56543
|
+
type: TIMEBACK_TYPES2.user,
|
|
55871
56544
|
email: data.studentEmail
|
|
55872
56545
|
},
|
|
55873
|
-
action:
|
|
56546
|
+
action: TIMEBACK_ACTIONS2.spentTime,
|
|
55874
56547
|
...data.runId ? { session: `urn:uuid:${data.runId}` } : {},
|
|
55875
56548
|
object: {
|
|
55876
56549
|
id: caliper.buildActivityUrl(data),
|
|
55877
|
-
type:
|
|
56550
|
+
type: TIMEBACK_TYPES2.activityContext,
|
|
55878
56551
|
subject: data.subject,
|
|
55879
56552
|
app: {
|
|
55880
56553
|
name: data.appName
|
|
@@ -55887,16 +56560,16 @@ function createCaliperNamespace(client) {
|
|
|
55887
56560
|
},
|
|
55888
56561
|
generated: {
|
|
55889
56562
|
id: `urn:timeback:metrics:time-spent-${crypto.randomUUID()}`,
|
|
55890
|
-
type:
|
|
56563
|
+
type: TIMEBACK_TYPES2.timeSpentMetricsCollection,
|
|
55891
56564
|
items: [
|
|
55892
|
-
{ type:
|
|
56565
|
+
{ type: TIME_METRIC_TYPES2.active, value: data.activeTimeSeconds },
|
|
55893
56566
|
...data.inactiveTimeSeconds !== undefined ? [
|
|
55894
56567
|
{
|
|
55895
|
-
type:
|
|
56568
|
+
type: TIME_METRIC_TYPES2.inactive,
|
|
55896
56569
|
value: data.inactiveTimeSeconds
|
|
55897
56570
|
}
|
|
55898
56571
|
] : [],
|
|
55899
|
-
...data.wasteTimeSeconds !== undefined ? [{ type:
|
|
56572
|
+
...data.wasteTimeSeconds !== undefined ? [{ type: TIME_METRIC_TYPES2.waste, value: data.wasteTimeSeconds }] : []
|
|
55900
56573
|
],
|
|
55901
56574
|
...data.extensions ? { extensions: data.extensions } : {}
|
|
55902
56575
|
},
|
|
@@ -55907,7 +56580,8 @@ function createCaliperNamespace(client) {
|
|
|
55907
56580
|
buildActivityUrl: (data) => {
|
|
55908
56581
|
const base = data.sensorUrl.replace(/\/$/, "");
|
|
55909
56582
|
return `${base}/activities/${encodeURIComponent(data.courseId)}/${encodeURIComponent(data.activityId)}`;
|
|
55910
|
-
}
|
|
56583
|
+
},
|
|
56584
|
+
buildCourseUrl: (courseId) => urls.course(courseId)
|
|
55911
56585
|
};
|
|
55912
56586
|
return caliper;
|
|
55913
56587
|
}
|
|
@@ -56030,17 +56704,17 @@ async function listOneRosterCollection(client, endpoint, collectionKey, options)
|
|
|
56030
56704
|
function createOneRosterNamespace(client) {
|
|
56031
56705
|
return {
|
|
56032
56706
|
classes: {
|
|
56033
|
-
create: async (data) => client["request"](
|
|
56707
|
+
create: async (data) => client["request"](ONEROSTER_ENDPOINTS2.classes, "POST", { class: data }),
|
|
56034
56708
|
get: async (sourcedId) => {
|
|
56035
|
-
const res = await client["request"](`${
|
|
56709
|
+
const res = await client["request"](`${ONEROSTER_ENDPOINTS2.classes}/${sourcedId}`, "GET");
|
|
56036
56710
|
return res.class;
|
|
56037
56711
|
},
|
|
56038
56712
|
update: async (sourcedId, data) => {
|
|
56039
|
-
const res = await client["request"](`${
|
|
56713
|
+
const res = await client["request"](`${ONEROSTER_ENDPOINTS2.classes}/${sourcedId}`, "PUT", { class: data });
|
|
56040
56714
|
return res.class;
|
|
56041
56715
|
},
|
|
56042
56716
|
listByCourse: async (courseSourcedId) => {
|
|
56043
|
-
const res = await client["request"](`${
|
|
56717
|
+
const res = await client["request"](`${ONEROSTER_ENDPOINTS2.courses}/${courseSourcedId}/classes`, "GET");
|
|
56044
56718
|
return res.classes;
|
|
56045
56719
|
},
|
|
56046
56720
|
listByStudent: async (userSourcedId, options) => {
|
|
@@ -56051,7 +56725,7 @@ function createOneRosterNamespace(client) {
|
|
|
56051
56725
|
if (options?.offset) {
|
|
56052
56726
|
queryParams.set("offset", String(options.offset));
|
|
56053
56727
|
}
|
|
56054
|
-
const endpoint = `${
|
|
56728
|
+
const endpoint = `${ONEROSTER_ENDPOINTS2.users}/${userSourcedId}/classes`;
|
|
56055
56729
|
const url2 = queryParams.toString() ? `${endpoint}?${queryParams}` : endpoint;
|
|
56056
56730
|
const res = await client["request"](url2, "GET");
|
|
56057
56731
|
return res.classes || [];
|
|
@@ -56059,7 +56733,7 @@ function createOneRosterNamespace(client) {
|
|
|
56059
56733
|
},
|
|
56060
56734
|
enrollments: {
|
|
56061
56735
|
get: async (sourcedId) => {
|
|
56062
|
-
const response = await client["request"](`${
|
|
56736
|
+
const response = await client["request"](`${ONEROSTER_ENDPOINTS2.enrollments}/${sourcedId}`, "GET");
|
|
56063
56737
|
return response.enrollment;
|
|
56064
56738
|
},
|
|
56065
56739
|
listByClass: async (classSourcedId, options) => {
|
|
@@ -56075,7 +56749,7 @@ function createOneRosterNamespace(client) {
|
|
|
56075
56749
|
if (options?.offset) {
|
|
56076
56750
|
queryParams.set("offset", String(options.offset));
|
|
56077
56751
|
}
|
|
56078
|
-
const url2 = `${
|
|
56752
|
+
const url2 = `${ONEROSTER_ENDPOINTS2.enrollments}?${queryParams}`;
|
|
56079
56753
|
try {
|
|
56080
56754
|
const response = await client["request"](url2, "GET");
|
|
56081
56755
|
return response.enrollments || [];
|
|
@@ -56108,7 +56782,7 @@ function createOneRosterNamespace(client) {
|
|
|
56108
56782
|
}
|
|
56109
56783
|
queryParams.set("filter", filters.join(" AND "));
|
|
56110
56784
|
queryParams.set("limit", "3000");
|
|
56111
|
-
const url2 = `${
|
|
56785
|
+
const url2 = `${ONEROSTER_ENDPOINTS2.enrollments}?${queryParams}`;
|
|
56112
56786
|
const response = await client["request"](url2, "GET");
|
|
56113
56787
|
return response.enrollments || [];
|
|
56114
56788
|
}));
|
|
@@ -56138,72 +56812,72 @@ function createOneRosterNamespace(client) {
|
|
|
56138
56812
|
throw error;
|
|
56139
56813
|
}
|
|
56140
56814
|
},
|
|
56141
|
-
create: async (data) => client["request"](
|
|
56815
|
+
create: async (data) => client["request"](ONEROSTER_ENDPOINTS2.enrollments, "POST", { enrollment: data }),
|
|
56142
56816
|
update: async (sourcedId, data) => {
|
|
56143
|
-
await client["request"](`${
|
|
56817
|
+
await client["request"](`${ONEROSTER_ENDPOINTS2.enrollments}/${sourcedId}`, "PUT", {
|
|
56144
56818
|
enrollment: data
|
|
56145
56819
|
});
|
|
56146
56820
|
},
|
|
56147
56821
|
delete: async (sourcedId) => {
|
|
56148
|
-
await client["request"](`${
|
|
56822
|
+
await client["request"](`${ONEROSTER_ENDPOINTS2.enrollments}/${sourcedId}`, "DELETE");
|
|
56149
56823
|
}
|
|
56150
56824
|
},
|
|
56151
56825
|
organizations: {
|
|
56152
|
-
create: async (data) => client["request"](
|
|
56153
|
-
get: async (sourcedId) => client["request"](`${
|
|
56154
|
-
update: async (sourcedId, data) => client["request"](`${
|
|
56826
|
+
create: async (data) => client["request"](ONEROSTER_ENDPOINTS2.organizations, "POST", data),
|
|
56827
|
+
get: async (sourcedId) => client["request"](`${ONEROSTER_ENDPOINTS2.organizations}/${sourcedId}`, "GET").then((res) => res.org),
|
|
56828
|
+
update: async (sourcedId, data) => client["request"](`${ONEROSTER_ENDPOINTS2.organizations}/${sourcedId}`, "PUT", data)
|
|
56155
56829
|
},
|
|
56156
56830
|
courses: {
|
|
56157
|
-
create: async (data) => client["request"](
|
|
56831
|
+
create: async (data) => client["request"](ONEROSTER_ENDPOINTS2.courses, "POST", data),
|
|
56158
56832
|
get: async (sourcedId) => {
|
|
56159
|
-
const response = await client["request"](`${
|
|
56833
|
+
const response = await client["request"](`${ONEROSTER_ENDPOINTS2.courses}/${sourcedId}`, "GET");
|
|
56160
56834
|
return response.course;
|
|
56161
56835
|
},
|
|
56162
56836
|
update: async (sourcedId, data) => {
|
|
56163
|
-
await client["request"](`${
|
|
56837
|
+
await client["request"](`${ONEROSTER_ENDPOINTS2.courses}/${sourcedId}`, "PUT", {
|
|
56164
56838
|
course: data
|
|
56165
56839
|
});
|
|
56166
56840
|
}
|
|
56167
56841
|
},
|
|
56168
56842
|
courseComponents: {
|
|
56169
|
-
create: async (data) => client["request"](
|
|
56843
|
+
create: async (data) => client["request"](ONEROSTER_ENDPOINTS2.courseComponents, "POST", data),
|
|
56170
56844
|
get: async (sourcedId) => {
|
|
56171
|
-
const response = await client["request"](`${
|
|
56845
|
+
const response = await client["request"](`${ONEROSTER_ENDPOINTS2.courseComponents}/${sourcedId}`, "GET");
|
|
56172
56846
|
return response.courseComponent;
|
|
56173
56847
|
},
|
|
56174
56848
|
update: async (sourcedId, data) => {
|
|
56175
|
-
await client["request"](`${
|
|
56849
|
+
await client["request"](`${ONEROSTER_ENDPOINTS2.courseComponents}/${sourcedId}`, "PUT", { courseComponent: data });
|
|
56176
56850
|
}
|
|
56177
56851
|
},
|
|
56178
56852
|
resources: {
|
|
56179
|
-
create: async (data) => client["request"](
|
|
56853
|
+
create: async (data) => client["request"](ONEROSTER_ENDPOINTS2.resources, "POST", data),
|
|
56180
56854
|
get: async (sourcedId) => {
|
|
56181
|
-
const response = await client["request"](`${
|
|
56855
|
+
const response = await client["request"](`${ONEROSTER_ENDPOINTS2.resources}/${sourcedId}`, "GET");
|
|
56182
56856
|
return response.resource;
|
|
56183
56857
|
},
|
|
56184
56858
|
update: async (sourcedId, data) => {
|
|
56185
|
-
await client["request"](`${
|
|
56859
|
+
await client["request"](`${ONEROSTER_ENDPOINTS2.resources}/${sourcedId}`, "PUT", {
|
|
56186
56860
|
resource: data
|
|
56187
56861
|
});
|
|
56188
56862
|
},
|
|
56189
56863
|
delete: async (sourcedId) => {
|
|
56190
|
-
await client["request"](`${
|
|
56864
|
+
await client["request"](`${ONEROSTER_ENDPOINTS2.resources}/${sourcedId}`, "DELETE");
|
|
56191
56865
|
}
|
|
56192
56866
|
},
|
|
56193
56867
|
componentResources: {
|
|
56194
|
-
create: async (data) => client["request"](
|
|
56868
|
+
create: async (data) => client["request"](ONEROSTER_ENDPOINTS2.componentResources, "POST", data),
|
|
56195
56869
|
get: async (sourcedId) => {
|
|
56196
|
-
const response = await client["request"](`${
|
|
56870
|
+
const response = await client["request"](`${ONEROSTER_ENDPOINTS2.componentResources}/${sourcedId}`, "GET");
|
|
56197
56871
|
return response.componentResource;
|
|
56198
56872
|
},
|
|
56199
56873
|
update: async (sourcedId, data) => {
|
|
56200
|
-
await client["request"](`${
|
|
56874
|
+
await client["request"](`${ONEROSTER_ENDPOINTS2.componentResources}/${sourcedId}`, "PUT", { componentResource: data });
|
|
56201
56875
|
}
|
|
56202
56876
|
},
|
|
56203
56877
|
assessmentLineItems: {
|
|
56204
56878
|
list: async (options) => {
|
|
56205
56879
|
try {
|
|
56206
|
-
return await listOneRosterCollection(client,
|
|
56880
|
+
return await listOneRosterCollection(client, ONEROSTER_ENDPOINTS2.assessmentLineItems, "assessmentLineItems", options);
|
|
56207
56881
|
} catch (error) {
|
|
56208
56882
|
logTimebackError("list assessment line items", error, {
|
|
56209
56883
|
filter: options?.filter
|
|
@@ -56211,14 +56885,14 @@ function createOneRosterNamespace(client) {
|
|
|
56211
56885
|
return [];
|
|
56212
56886
|
}
|
|
56213
56887
|
},
|
|
56214
|
-
create: async (data) => client["request"](
|
|
56888
|
+
create: async (data) => client["request"](ONEROSTER_ENDPOINTS2.assessmentLineItems, "POST", { assessmentLineItem: data }),
|
|
56215
56889
|
get: async (sourcedId) => {
|
|
56216
|
-
const response = await client["request"](`${
|
|
56890
|
+
const response = await client["request"](`${ONEROSTER_ENDPOINTS2.assessmentLineItems}/${sourcedId}`, "GET");
|
|
56217
56891
|
return response.assessmentLineItem;
|
|
56218
56892
|
},
|
|
56219
56893
|
findOrCreate: async (sourcedId, data) => {
|
|
56220
56894
|
try {
|
|
56221
|
-
const response = await client["request"](`${
|
|
56895
|
+
const response = await client["request"](`${ONEROSTER_ENDPOINTS2.assessmentLineItems}/${sourcedId}`, "GET");
|
|
56222
56896
|
return response.assessmentLineItem;
|
|
56223
56897
|
} catch {
|
|
56224
56898
|
const createData = {
|
|
@@ -56227,7 +56901,7 @@ function createOneRosterNamespace(client) {
|
|
|
56227
56901
|
dateLastModified: new Date().toISOString()
|
|
56228
56902
|
};
|
|
56229
56903
|
try {
|
|
56230
|
-
const response = await client["request"](
|
|
56904
|
+
const response = await client["request"](ONEROSTER_ENDPOINTS2.assessmentLineItems, "POST", { assessmentLineItem: createData });
|
|
56231
56905
|
if (!response.sourcedIdPairs?.allocatedSourcedId) {
|
|
56232
56906
|
throw new Error("Invalid response from OneRoster API - missing allocatedSourcedId");
|
|
56233
56907
|
}
|
|
@@ -56242,7 +56916,7 @@ function createOneRosterNamespace(client) {
|
|
|
56242
56916
|
assessmentResults: {
|
|
56243
56917
|
list: async (options) => {
|
|
56244
56918
|
try {
|
|
56245
|
-
return await listOneRosterCollection(client,
|
|
56919
|
+
return await listOneRosterCollection(client, ONEROSTER_ENDPOINTS2.assessmentResults, "assessmentResults", options);
|
|
56246
56920
|
} catch (error) {
|
|
56247
56921
|
logTimebackError("list assessment results", error, {
|
|
56248
56922
|
filter: options?.filter
|
|
@@ -56250,10 +56924,11 @@ function createOneRosterNamespace(client) {
|
|
|
56250
56924
|
return [];
|
|
56251
56925
|
}
|
|
56252
56926
|
},
|
|
56253
|
-
|
|
56927
|
+
listOrThrow: async (options) => await listOneRosterCollection(client, ONEROSTER_ENDPOINTS2.assessmentResults, "assessmentResults", options),
|
|
56928
|
+
create: async (data) => client["request"](ONEROSTER_ENDPOINTS2.assessmentResults, "POST", { assessmentResult: data }),
|
|
56254
56929
|
listByStudent: async (studentSourcedId, options) => {
|
|
56255
56930
|
try {
|
|
56256
|
-
return await listOneRosterCollection(client,
|
|
56931
|
+
return await listOneRosterCollection(client, ONEROSTER_ENDPOINTS2.assessmentResults, "assessmentResults", {
|
|
56257
56932
|
limit: options?.limit,
|
|
56258
56933
|
offset: options?.offset,
|
|
56259
56934
|
fields: options?.fields,
|
|
@@ -56272,54 +56947,22 @@ function createOneRosterNamespace(client) {
|
|
|
56272
56947
|
sourcedId,
|
|
56273
56948
|
dateLastModified: new Date().toISOString()
|
|
56274
56949
|
};
|
|
56275
|
-
return client["request"](`${
|
|
56950
|
+
return client["request"](`${ONEROSTER_ENDPOINTS2.assessmentResults}/${sourcedId}`, "PUT", { assessmentResult });
|
|
56276
56951
|
},
|
|
56277
56952
|
get: async (sourcedId) => {
|
|
56278
|
-
const response = await client["request"](`${
|
|
56953
|
+
const response = await client["request"](`${ONEROSTER_ENDPOINTS2.assessmentResults}/${sourcedId}`, "GET");
|
|
56279
56954
|
return response.assessmentResult;
|
|
56280
56955
|
},
|
|
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
|
-
}
|
|
56956
|
+
update: async (sourcedId, data) => client["request"](`${ONEROSTER_ENDPOINTS2.assessmentResults}/${sourcedId}`, "PUT", data)
|
|
56314
56957
|
},
|
|
56315
56958
|
users: {
|
|
56316
|
-
create: async (data) => client["request"](
|
|
56959
|
+
create: async (data) => client["request"](ONEROSTER_ENDPOINTS2.users, "POST", {
|
|
56317
56960
|
user: data
|
|
56318
56961
|
}),
|
|
56319
|
-
get: async (sourcedId) => client["request"](`${
|
|
56962
|
+
get: async (sourcedId) => client["request"](`${ONEROSTER_ENDPOINTS2.users}/${sourcedId}`, "GET").then((res) => res.user),
|
|
56320
56963
|
findByEmail: async (email) => {
|
|
56321
56964
|
const params = new URLSearchParams({ filter: `email='${email}'` });
|
|
56322
|
-
const response = await client["request"](`${
|
|
56965
|
+
const response = await client["request"](`${ONEROSTER_ENDPOINTS2.users}?${params}`, "GET");
|
|
56323
56966
|
if (!response || !response.users || !Array.isArray(response.users)) {
|
|
56324
56967
|
throw new Error(`Invalid response format from OneRoster API when searching for user with email: ${email}. Expected { users: [...] } but received: ${JSON.stringify(response)}`);
|
|
56325
56968
|
}
|
|
@@ -56338,7 +56981,7 @@ function createOneRosterNamespace(client) {
|
|
|
56338
56981
|
const results = await Promise.all(batches.map(async (batch) => {
|
|
56339
56982
|
const filter = batch.map((id) => `sourcedId='${escapeFilterValue(id)}'`).join(" OR ");
|
|
56340
56983
|
const params = new URLSearchParams({ filter });
|
|
56341
|
-
const response = await client["request"](`${
|
|
56984
|
+
const response = await client["request"](`${ONEROSTER_ENDPOINTS2.users}?${params}`, "GET");
|
|
56342
56985
|
return response.users || [];
|
|
56343
56986
|
}));
|
|
56344
56987
|
return results.flat();
|
|
@@ -56355,11 +56998,11 @@ function createOneRosterNamespace(client) {
|
|
|
56355
56998
|
function createQtiNamespace(client) {
|
|
56356
56999
|
return {
|
|
56357
57000
|
items: {
|
|
56358
|
-
create: async (data) => client["requestQti"](
|
|
56359
|
-
get: async (identifier) => client["requestQti"](`${
|
|
56360
|
-
update: async (identifier, data) => client["requestQti"](`${
|
|
57001
|
+
create: async (data) => client["requestQti"](QTI_ENDPOINTS2.assessmentItems, "POST", data),
|
|
57002
|
+
get: async (identifier) => client["requestQti"](`${QTI_ENDPOINTS2.assessmentItems}/${identifier}`, "GET"),
|
|
57003
|
+
update: async (identifier, data) => client["requestQti"](`${QTI_ENDPOINTS2.assessmentItems}/${identifier}`, "PUT", data),
|
|
56361
57004
|
delete: async (identifier) => {
|
|
56362
|
-
await client["requestQti"](`${
|
|
57005
|
+
await client["requestQti"](`${QTI_ENDPOINTS2.assessmentItems}/${identifier}`, "DELETE");
|
|
56363
57006
|
}
|
|
56364
57007
|
},
|
|
56365
57008
|
tests: {
|
|
@@ -56381,25 +57024,25 @@ function createQtiNamespace(client) {
|
|
|
56381
57024
|
params.set("order", options.order);
|
|
56382
57025
|
}
|
|
56383
57026
|
const query = params.toString();
|
|
56384
|
-
return client["requestQti"](`${
|
|
57027
|
+
return client["requestQti"](`${QTI_ENDPOINTS2.assessmentTests}${query ? `?${query}` : ""}`, "GET");
|
|
56385
57028
|
},
|
|
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"](`${
|
|
57029
|
+
create: async (data) => client["requestQti"](QTI_ENDPOINTS2.assessmentTests, "POST", data),
|
|
57030
|
+
get: async (identifier) => client["requestQti"](`${QTI_ENDPOINTS2.assessmentTests}/${identifier}`, "GET"),
|
|
57031
|
+
getQuestions: async (identifier) => client["requestQti"](`${QTI_ENDPOINTS2.assessmentTests}/${identifier}/questions`, "GET"),
|
|
57032
|
+
update: async (identifier, data) => client["requestQti"](`${QTI_ENDPOINTS2.assessmentTests}/${identifier}`, "PUT", data),
|
|
56390
57033
|
delete: async (identifier) => {
|
|
56391
|
-
await client["requestQti"](`${
|
|
57034
|
+
await client["requestQti"](`${QTI_ENDPOINTS2.assessmentTests}/${identifier}`, "DELETE");
|
|
56392
57035
|
},
|
|
56393
|
-
addItem: async (testId, partId, sectionId, itemIdentifier) => client["requestQti"](`${
|
|
57036
|
+
addItem: async (testId, partId, sectionId, itemIdentifier) => client["requestQti"](`${QTI_ENDPOINTS2.assessmentTests}/${testId}/test-parts/${partId}/sections/${sectionId}/items`, "POST", { identifier: itemIdentifier }),
|
|
56394
57037
|
removeItem: async (testId, partId, sectionId, itemIdentifier) => {
|
|
56395
|
-
await client["requestQti"](`${
|
|
57038
|
+
await client["requestQti"](`${QTI_ENDPOINTS2.assessmentTests}/${testId}/test-parts/${partId}/sections/${sectionId}/items/${itemIdentifier}`, "DELETE");
|
|
56396
57039
|
},
|
|
56397
|
-
reorderItems: async (testId, partId, sectionId, items) => client["requestQti"](`${
|
|
57040
|
+
reorderItems: async (testId, partId, sectionId, items) => client["requestQti"](`${QTI_ENDPOINTS2.assessmentTests}/${testId}/test-parts/${partId}/sections/${sectionId}/items/order`, "PUT", { items })
|
|
56398
57041
|
}
|
|
56399
57042
|
};
|
|
56400
57043
|
}
|
|
56401
57044
|
function toCaliperSubject(subject) {
|
|
56402
|
-
return
|
|
57045
|
+
return isTimebackSubject3(subject) ? subject : "None";
|
|
56403
57046
|
}
|
|
56404
57047
|
function buildAdminEventMetadata({
|
|
56405
57048
|
reason,
|
|
@@ -56463,7 +57106,7 @@ class AdminEventRecorder {
|
|
|
56463
57106
|
defaultActivityId: "playcademy-admin-manual-xp",
|
|
56464
57107
|
eventKind: "remediation-xp"
|
|
56465
57108
|
});
|
|
56466
|
-
const courseUrl = createOneRosterUrls(
|
|
57109
|
+
const courseUrl = createOneRosterUrls(TIMEBACK_API_URLS2[this.environment]).course(data.courseId);
|
|
56467
57110
|
await this.caliper.emitActivityEvent({
|
|
56468
57111
|
studentId: ctx.student.id,
|
|
56469
57112
|
studentEmail: ctx.student.email,
|
|
@@ -56513,7 +57156,7 @@ class AdminEventRecorder {
|
|
|
56513
57156
|
defaultActivityId: "playcademy-admin-mastery-adjustment",
|
|
56514
57157
|
eventKind: "remediation-mastery"
|
|
56515
57158
|
});
|
|
56516
|
-
const courseUrl = createOneRosterUrls(
|
|
57159
|
+
const courseUrl = createOneRosterUrls(TIMEBACK_API_URLS2[this.environment]).course(data.courseId);
|
|
56517
57160
|
await this.caliper.emitActivityEvent({
|
|
56518
57161
|
studentId: ctx.student.id,
|
|
56519
57162
|
studentEmail: ctx.student.email,
|
|
@@ -56547,9 +57190,9 @@ class TimebackCache {
|
|
|
56547
57190
|
maxSize;
|
|
56548
57191
|
name;
|
|
56549
57192
|
constructor(options = {}) {
|
|
56550
|
-
this.defaultTTL = options.defaultTTL ||
|
|
56551
|
-
this.maxSize = options.maxSize ||
|
|
56552
|
-
this.name = options.name ||
|
|
57193
|
+
this.defaultTTL = options.defaultTTL || CACHE_DEFAULTS2.defaultTTL;
|
|
57194
|
+
this.maxSize = options.maxSize || CACHE_DEFAULTS2.defaultMaxSize;
|
|
57195
|
+
this.name = options.name || CACHE_DEFAULTS2.defaultName;
|
|
56553
57196
|
}
|
|
56554
57197
|
get(key) {
|
|
56555
57198
|
const entry = this.cache.get(key);
|
|
@@ -56640,23 +57283,23 @@ class TimebackCacheManager {
|
|
|
56640
57283
|
enrollmentCache;
|
|
56641
57284
|
constructor() {
|
|
56642
57285
|
this.studentCache = new TimebackCache({
|
|
56643
|
-
defaultTTL:
|
|
56644
|
-
maxSize:
|
|
57286
|
+
defaultTTL: CACHE_DEFAULTS2.studentTTL,
|
|
57287
|
+
maxSize: CACHE_DEFAULTS2.studentMaxSize,
|
|
56645
57288
|
name: "StudentCache"
|
|
56646
57289
|
});
|
|
56647
57290
|
this.assessmentLineItemCache = new TimebackCache({
|
|
56648
|
-
defaultTTL:
|
|
56649
|
-
maxSize:
|
|
57291
|
+
defaultTTL: CACHE_DEFAULTS2.assessmentTTL,
|
|
57292
|
+
maxSize: CACHE_DEFAULTS2.assessmentMaxSize,
|
|
56650
57293
|
name: "AssessmentLineItemCache"
|
|
56651
57294
|
});
|
|
56652
57295
|
this.resourceMasteryCache = new TimebackCache({
|
|
56653
|
-
defaultTTL:
|
|
56654
|
-
maxSize:
|
|
57296
|
+
defaultTTL: CACHE_DEFAULTS2.assessmentTTL,
|
|
57297
|
+
maxSize: CACHE_DEFAULTS2.assessmentMaxSize,
|
|
56655
57298
|
name: "ResourceMasteryCache"
|
|
56656
57299
|
});
|
|
56657
57300
|
this.enrollmentCache = new TimebackCache({
|
|
56658
|
-
defaultTTL:
|
|
56659
|
-
maxSize:
|
|
57301
|
+
defaultTTL: CACHE_DEFAULTS2.enrollmentTTL,
|
|
57302
|
+
maxSize: CACHE_DEFAULTS2.enrollmentMaxSize,
|
|
56660
57303
|
name: "EnrollmentCache"
|
|
56661
57304
|
});
|
|
56662
57305
|
}
|
|
@@ -56912,18 +57555,18 @@ class MasteryTracker {
|
|
|
56912
57555
|
await this.onerosterNamespace.assessmentLineItems.findOrCreate(lineItemId, {
|
|
56913
57556
|
sourcedId: lineItemId,
|
|
56914
57557
|
title: "Mastery Completion",
|
|
56915
|
-
status:
|
|
57558
|
+
status: ONEROSTER_STATUS2.active,
|
|
56916
57559
|
...classId ? { class: { sourcedId: classId } } : { course: { sourcedId: ids.course } },
|
|
56917
57560
|
...ids.componentResource ? { componentResource: { sourcedId: ids.componentResource } } : {}
|
|
56918
57561
|
});
|
|
56919
57562
|
await this.onerosterNamespace.assessmentResults.upsert(resultId, {
|
|
56920
57563
|
sourcedId: resultId,
|
|
56921
|
-
status:
|
|
57564
|
+
status: ONEROSTER_STATUS2.active,
|
|
56922
57565
|
assessmentLineItem: { sourcedId: lineItemId },
|
|
56923
57566
|
student: { sourcedId: studentId },
|
|
56924
57567
|
score: 100,
|
|
56925
57568
|
scoreDate: new Date().toISOString(),
|
|
56926
|
-
scoreStatus:
|
|
57569
|
+
scoreStatus: SCORE_STATUS2.fullyGraded,
|
|
56927
57570
|
inProgress: "false",
|
|
56928
57571
|
metadata: {
|
|
56929
57572
|
isMasteryCompletion: true,
|
|
@@ -56935,6 +57578,7 @@ class MasteryTracker {
|
|
|
56935
57578
|
"app.timeback.line_item_id": lineItemId,
|
|
56936
57579
|
"app.timeback.result_id": resultId
|
|
56937
57580
|
});
|
|
57581
|
+
return true;
|
|
56938
57582
|
} catch (error) {
|
|
56939
57583
|
addEvent("timeback.mastery_completion_failed", {
|
|
56940
57584
|
"app.timeback.student_id": studentId,
|
|
@@ -56942,6 +57586,7 @@ class MasteryTracker {
|
|
|
56942
57586
|
"exception.type": errorType(error),
|
|
56943
57587
|
"app.error.message": errorMessage(error)
|
|
56944
57588
|
});
|
|
57589
|
+
return false;
|
|
56945
57590
|
}
|
|
56946
57591
|
}
|
|
56947
57592
|
async revokeCompletionEntry(studentId, courseId, classId, appName) {
|
|
@@ -56952,18 +57597,18 @@ class MasteryTracker {
|
|
|
56952
57597
|
await this.onerosterNamespace.assessmentLineItems.findOrCreate(lineItemId, {
|
|
56953
57598
|
sourcedId: lineItemId,
|
|
56954
57599
|
title: "Mastery Completion",
|
|
56955
|
-
status:
|
|
57600
|
+
status: ONEROSTER_STATUS2.active,
|
|
56956
57601
|
...classId ? { class: { sourcedId: classId } } : { course: { sourcedId: ids.course } },
|
|
56957
57602
|
...ids.componentResource ? { componentResource: { sourcedId: ids.componentResource } } : {}
|
|
56958
57603
|
});
|
|
56959
57604
|
await this.onerosterNamespace.assessmentResults.upsert(resultId, {
|
|
56960
57605
|
sourcedId: resultId,
|
|
56961
|
-
status:
|
|
57606
|
+
status: ONEROSTER_STATUS2.active,
|
|
56962
57607
|
assessmentLineItem: { sourcedId: lineItemId },
|
|
56963
57608
|
student: { sourcedId: studentId },
|
|
56964
57609
|
score: 0,
|
|
56965
57610
|
scoreDate: new Date().toISOString(),
|
|
56966
|
-
scoreStatus:
|
|
57611
|
+
scoreStatus: SCORE_STATUS2.notSubmitted,
|
|
56967
57612
|
inProgress: "true",
|
|
56968
57613
|
metadata: {
|
|
56969
57614
|
isMasteryCompletion: true,
|
|
@@ -56998,7 +57643,7 @@ class MasteryTracker {
|
|
|
56998
57643
|
if (!playcademyMetadata) {
|
|
56999
57644
|
return;
|
|
57000
57645
|
}
|
|
57001
|
-
const masterableUnits =
|
|
57646
|
+
const masterableUnits = isPlaycademyResourceMetadata2(playcademyMetadata) ? playcademyMetadata.mastery?.masterableUnits : undefined;
|
|
57002
57647
|
this.cacheManager.setResourceMasterableUnits(resourceId, masterableUnits ?? null);
|
|
57003
57648
|
return masterableUnits;
|
|
57004
57649
|
} catch (error) {
|
|
@@ -57078,13 +57723,11 @@ function validateSessionData(sessionData) {
|
|
|
57078
57723
|
|
|
57079
57724
|
class ProgressRecorder {
|
|
57080
57725
|
studentResolver;
|
|
57081
|
-
cacheManager;
|
|
57082
57726
|
onerosterNamespace;
|
|
57083
57727
|
caliperNamespace;
|
|
57084
57728
|
masteryTracker;
|
|
57085
|
-
constructor(studentResolver,
|
|
57729
|
+
constructor(studentResolver, onerosterNamespace, caliperNamespace, masteryTracker) {
|
|
57086
57730
|
this.studentResolver = studentResolver;
|
|
57087
|
-
this.cacheManager = cacheManager;
|
|
57088
57731
|
this.onerosterNamespace = onerosterNamespace;
|
|
57089
57732
|
this.caliperNamespace = caliperNamespace;
|
|
57090
57733
|
this.masteryTracker = masteryTracker;
|
|
@@ -57093,26 +57736,37 @@ class ProgressRecorder {
|
|
|
57093
57736
|
validateProgressData(progressData);
|
|
57094
57737
|
const { ids, activityId, activityName, courseName, student } = await this.resolveContext(courseId, studentIdentifier, progressData);
|
|
57095
57738
|
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
|
|
57739
|
+
const { totalQuestions, correctQuestions, xpEarned = 0, attemptNumber } = progressData;
|
|
57740
|
+
const activityUrl = this.caliperNamespace.buildActivityUrl({
|
|
57741
|
+
courseId: ids.course,
|
|
57742
|
+
activityId,
|
|
57743
|
+
sensorUrl: progressData.sensorUrl
|
|
57110
57744
|
});
|
|
57745
|
+
const courseUrl = this.caliperNamespace.buildCourseUrl(ids.course);
|
|
57746
|
+
let caliperLineItemId;
|
|
57747
|
+
try {
|
|
57748
|
+
caliperLineItemId = computeCaliperLineItemId(activityUrl, ids.course, courseUrl);
|
|
57749
|
+
} catch (error) {
|
|
57750
|
+
setAttributes({ "app.timeback.course_id_not_url_safe": true });
|
|
57751
|
+
throw error;
|
|
57752
|
+
}
|
|
57753
|
+
const legacyLineItemId = `${ids.course}-${activityId}-assessment`;
|
|
57754
|
+
const [currentAttemptNumber, masteryProgress] = await Promise.all([
|
|
57755
|
+
this.resolveAttemptNumber(attemptNumber, studentId, caliperLineItemId, legacyLineItemId),
|
|
57756
|
+
this.masteryTracker.checkProgress({
|
|
57757
|
+
studentId,
|
|
57758
|
+
courseId,
|
|
57759
|
+
resourceId: ids.resource,
|
|
57760
|
+
masteredUnits: progressData.masteredUnits ?? 0,
|
|
57761
|
+
masteredUnitsAbsolute: progressData.masteredUnitsAbsolute
|
|
57762
|
+
})
|
|
57763
|
+
]);
|
|
57764
|
+
setAttributes({ "app.timeback.attempt_number": currentAttemptNumber });
|
|
57765
|
+
let extensions = progressData.extensions;
|
|
57111
57766
|
const effectiveMasteredUnits = masteryProgress ? masteryProgress.effectiveDelta : progressData.masteredUnits ?? 0;
|
|
57112
57767
|
let pctCompleteApp;
|
|
57113
57768
|
let masteryAchieved = false;
|
|
57114
|
-
let
|
|
57115
|
-
const inProgress = "false";
|
|
57769
|
+
let completionEntryWritten = false;
|
|
57116
57770
|
const warnings = masteryProgress?.writeWarning ? [masteryProgress.writeWarning] : undefined;
|
|
57117
57771
|
if (masteryProgress) {
|
|
57118
57772
|
masteryAchieved = masteryProgress.masteryAchieved;
|
|
@@ -57121,32 +57775,12 @@ class ProgressRecorder {
|
|
|
57121
57775
|
...extensions,
|
|
57122
57776
|
...pctCompleteApp !== undefined ? { pctCompleteApp } : {}
|
|
57123
57777
|
};
|
|
57124
|
-
if (masteryAchieved) {
|
|
57125
|
-
scoreStatus = SCORE_STATUS.fullyGraded;
|
|
57126
|
-
}
|
|
57127
57778
|
}
|
|
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);
|
|
57779
|
+
if (masteryAchieved) {
|
|
57780
|
+
setAttributes({ "app.timeback.mastery_achieved": true });
|
|
57147
57781
|
}
|
|
57148
57782
|
if (masteryAchieved) {
|
|
57149
|
-
await this.masteryTracker.createCompletionEntry(studentId, courseId, progressData.classId, progressData.appName);
|
|
57783
|
+
completionEntryWritten = await this.masteryTracker.createCompletionEntry(studentId, courseId, progressData.classId, progressData.appName);
|
|
57150
57784
|
await this.emitCourseCompletionHistoryEvent({
|
|
57151
57785
|
studentId,
|
|
57152
57786
|
studentEmail,
|
|
@@ -57162,30 +57796,36 @@ class ProgressRecorder {
|
|
|
57162
57796
|
if (masteryProgress?.masteryRevoked) {
|
|
57163
57797
|
await this.masteryTracker.revokeCompletionEntry(studentId, courseId, progressData.classId, progressData.appName);
|
|
57164
57798
|
}
|
|
57165
|
-
|
|
57166
|
-
|
|
57167
|
-
|
|
57168
|
-
|
|
57169
|
-
|
|
57170
|
-
|
|
57171
|
-
|
|
57172
|
-
|
|
57173
|
-
|
|
57174
|
-
|
|
57175
|
-
|
|
57176
|
-
|
|
57177
|
-
|
|
57178
|
-
|
|
57179
|
-
|
|
57180
|
-
|
|
57181
|
-
|
|
57799
|
+
try {
|
|
57800
|
+
await this.emitCaliperEvent({
|
|
57801
|
+
studentId,
|
|
57802
|
+
studentEmail,
|
|
57803
|
+
gameId: progressData.gameId,
|
|
57804
|
+
activityId,
|
|
57805
|
+
activityName,
|
|
57806
|
+
courseId: ids.course,
|
|
57807
|
+
courseName,
|
|
57808
|
+
totalQuestions,
|
|
57809
|
+
correctQuestions,
|
|
57810
|
+
xpEarned,
|
|
57811
|
+
masteredUnits: effectiveMasteredUnits || undefined,
|
|
57812
|
+
attemptNumber: currentAttemptNumber,
|
|
57813
|
+
objectId: activityUrl,
|
|
57814
|
+
progressData,
|
|
57815
|
+
extensions,
|
|
57816
|
+
runId: progressData.runId
|
|
57817
|
+
});
|
|
57818
|
+
} catch (error) {
|
|
57819
|
+
if (completionEntryWritten) {
|
|
57820
|
+
setAttributes({ "app.timeback.completion_orphaned": true });
|
|
57821
|
+
}
|
|
57822
|
+
throw error;
|
|
57823
|
+
}
|
|
57182
57824
|
return {
|
|
57183
57825
|
xpAwarded: xpEarned,
|
|
57184
57826
|
attemptNumber: currentAttemptNumber,
|
|
57185
57827
|
masteredUnitsApplied: effectiveMasteredUnits,
|
|
57186
57828
|
pctCompleteApp,
|
|
57187
|
-
scoreStatus,
|
|
57188
|
-
inProgress,
|
|
57189
57829
|
...warnings ? { warnings } : {}
|
|
57190
57830
|
};
|
|
57191
57831
|
}
|
|
@@ -57197,89 +57837,48 @@ class ProgressRecorder {
|
|
|
57197
57837
|
const student = await this.studentResolver.resolve(studentIdentifier, progressData.studentEmail);
|
|
57198
57838
|
return { ids, activityId, activityName, courseName, student };
|
|
57199
57839
|
}
|
|
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) {
|
|
57840
|
+
async resolveAttemptNumber(providedAttemptNumber, studentId, caliperLineItemId, legacyLineItemId) {
|
|
57210
57841
|
if (providedAttemptNumber) {
|
|
57842
|
+
setAttributes({ "app.timeback.attempt_source": "provided" });
|
|
57211
57843
|
return providedAttemptNumber;
|
|
57212
57844
|
}
|
|
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 } }
|
|
57845
|
+
const caliperAttempt = await this.getLatestActivityAttempt(studentId, caliperLineItemId, "attempt");
|
|
57846
|
+
if (caliperAttempt !== null) {
|
|
57847
|
+
const next = caliperAttempt + 1;
|
|
57848
|
+
setAttributes({
|
|
57849
|
+
"app.timeback.attempt_source": caliperAttempt > 0 ? "caliper" : "caliper_unreadable"
|
|
57225
57850
|
});
|
|
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
|
-
}
|
|
57851
|
+
if (caliperAttempt === 0) {
|
|
57852
|
+
setAttributes({
|
|
57853
|
+
"app.timeback.attempt_regressed": true,
|
|
57854
|
+
"app.timeback.attempt_emitted": next
|
|
57855
|
+
});
|
|
57237
57856
|
}
|
|
57238
|
-
|
|
57857
|
+
return next;
|
|
57239
57858
|
}
|
|
57240
|
-
|
|
57241
|
-
|
|
57242
|
-
|
|
57243
|
-
|
|
57244
|
-
|
|
57859
|
+
const legacyAttempt = await this.getLatestActivityAttempt(studentId, legacyLineItemId, "attemptNumber");
|
|
57860
|
+
if (legacyAttempt !== null) {
|
|
57861
|
+
setAttributes({
|
|
57862
|
+
"app.timeback.attempt_source": "legacy_seed",
|
|
57863
|
+
"app.timeback.legacy_seed_attempt": legacyAttempt
|
|
57864
|
+
});
|
|
57865
|
+
return legacyAttempt + 1;
|
|
57245
57866
|
}
|
|
57867
|
+
setAttributes({ "app.timeback.attempt_source": "first" });
|
|
57246
57868
|
return 1;
|
|
57247
57869
|
}
|
|
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
|
-
}
|
|
57870
|
+
async getLatestActivityAttempt(studentId, lineItemId, metadataField) {
|
|
57871
|
+
const results = await this.onerosterNamespace.assessmentResults.listOrThrow({
|
|
57872
|
+
filter: `student.sourcedId='${escapeFilterValue(studentId)}' AND assessmentLineItem.sourcedId='${escapeFilterValue(lineItemId)}' AND status='active'`,
|
|
57873
|
+
sort: "dateLastModified",
|
|
57874
|
+
orderBy: "desc",
|
|
57875
|
+
limit: 1
|
|
57282
57876
|
});
|
|
57877
|
+
const latest = results[0];
|
|
57878
|
+
if (!latest) {
|
|
57879
|
+
return null;
|
|
57880
|
+
}
|
|
57881
|
+
return latest.metadata?.[metadataField] || 0;
|
|
57283
57882
|
}
|
|
57284
57883
|
async emitCaliperEvent({
|
|
57285
57884
|
studentId,
|
|
@@ -57294,6 +57893,7 @@ class ProgressRecorder {
|
|
|
57294
57893
|
xpEarned,
|
|
57295
57894
|
masteredUnits,
|
|
57296
57895
|
attemptNumber,
|
|
57896
|
+
objectId,
|
|
57297
57897
|
progressData,
|
|
57298
57898
|
extensions,
|
|
57299
57899
|
runId
|
|
@@ -57311,12 +57911,18 @@ class ProgressRecorder {
|
|
|
57311
57911
|
xpEarned,
|
|
57312
57912
|
masteredUnits,
|
|
57313
57913
|
attemptNumber,
|
|
57914
|
+
objectId,
|
|
57915
|
+
process: true,
|
|
57314
57916
|
subject: progressData.subject,
|
|
57315
57917
|
appName: progressData.appName,
|
|
57316
57918
|
sensorUrl: progressData.sensorUrl,
|
|
57317
57919
|
extensions: extensions || progressData.extensions,
|
|
57318
57920
|
...runId ? { runId } : {}
|
|
57319
|
-
}).catch(
|
|
57921
|
+
}).catch((error) => {
|
|
57922
|
+
setAttributes({ "app.timeback.caliper_emit_failed": true });
|
|
57923
|
+
catchEvent("timeback.caliper_event_failed")(error);
|
|
57924
|
+
throw error;
|
|
57925
|
+
});
|
|
57320
57926
|
}
|
|
57321
57927
|
async emitCourseCompletionHistoryEvent(data) {
|
|
57322
57928
|
await this.caliperNamespace.emitActivityEvent({
|
|
@@ -57494,15 +58100,15 @@ class TimebackClient {
|
|
|
57494
58100
|
masteryTracker;
|
|
57495
58101
|
constructor(config2) {
|
|
57496
58102
|
this.baseUrl = TimebackClient.resolveBaseUrl(config2?.baseUrl);
|
|
57497
|
-
this.environment = process.env[
|
|
58103
|
+
this.environment = process.env[ENV_VARS2.environment] === "staging" ? "staging" : "production";
|
|
57498
58104
|
this.caliperUrl = TimebackClient.resolveCaliperUrl(config2?.caliperUrl, this.environment);
|
|
57499
58105
|
this.authUrl = config2?.credentials?.authUrl;
|
|
57500
58106
|
this.credentials = config2?.credentials;
|
|
57501
58107
|
this.qtiCredentials = config2?.qtiCredentials;
|
|
57502
58108
|
this.options = {
|
|
57503
|
-
retries: config2?.options?.retries ??
|
|
57504
|
-
cacheDuration: config2?.options?.cacheDuration ??
|
|
57505
|
-
timeout: config2?.options?.timeout ??
|
|
58109
|
+
retries: config2?.options?.retries ?? HTTP_DEFAULTS2.retries,
|
|
58110
|
+
cacheDuration: config2?.options?.cacheDuration ?? AUTH_DEFAULTS2.tokenCacheDuration,
|
|
58111
|
+
timeout: config2?.options?.timeout ?? HTTP_DEFAULTS2.timeout,
|
|
57506
58112
|
sensorUrl: config2?.options?.sensorUrl
|
|
57507
58113
|
};
|
|
57508
58114
|
this.oneroster = createOneRosterNamespace(this);
|
|
@@ -57512,7 +58118,7 @@ class TimebackClient {
|
|
|
57512
58118
|
this.cacheManager = new TimebackCacheManager;
|
|
57513
58119
|
this.studentResolver = new StudentResolver(this.cacheManager, this.oneroster);
|
|
57514
58120
|
this.masteryTracker = new MasteryTracker(this.cacheManager, this.oneroster, this.edubridge);
|
|
57515
|
-
this.progressRecorder = new ProgressRecorder(this.studentResolver, this.
|
|
58121
|
+
this.progressRecorder = new ProgressRecorder(this.studentResolver, this.oneroster, this.caliper, this.masteryTracker);
|
|
57516
58122
|
this.sessionRecorder = new SessionRecorder(this.studentResolver, this.caliper);
|
|
57517
58123
|
this.adminEventRecorder = new AdminEventRecorder(this.studentResolver, this.oneroster, this.caliper, this.environment);
|
|
57518
58124
|
if (this.credentials) {
|
|
@@ -57521,16 +58127,16 @@ class TimebackClient {
|
|
|
57521
58127
|
}
|
|
57522
58128
|
static async init(config2) {
|
|
57523
58129
|
let credentials = config2?.credentials;
|
|
57524
|
-
if (!credentials && process.env[
|
|
58130
|
+
if (!credentials && process.env[ENV_VARS2.clientId] && process.env[ENV_VARS2.clientSecret]) {
|
|
57525
58131
|
credentials = {
|
|
57526
|
-
clientId: process.env[
|
|
57527
|
-
clientSecret: process.env[
|
|
58132
|
+
clientId: process.env[ENV_VARS2.clientId],
|
|
58133
|
+
clientSecret: process.env[ENV_VARS2.clientSecret]
|
|
57528
58134
|
};
|
|
57529
58135
|
}
|
|
57530
|
-
const qtiCredentials = config2?.qtiCredentials ?? (process.env[
|
|
57531
|
-
clientId: process.env[
|
|
57532
|
-
clientSecret: process.env[
|
|
57533
|
-
authUrl: process.env[
|
|
58136
|
+
const qtiCredentials = config2?.qtiCredentials ?? (process.env[ENV_VARS2.qtiClientId] && process.env[ENV_VARS2.qtiClientSecret] && process.env[ENV_VARS2.qtiAuthUrl] ? {
|
|
58137
|
+
clientId: process.env[ENV_VARS2.qtiClientId],
|
|
58138
|
+
clientSecret: process.env[ENV_VARS2.qtiClientSecret],
|
|
58139
|
+
authUrl: process.env[ENV_VARS2.qtiAuthUrl]
|
|
57534
58140
|
} : undefined);
|
|
57535
58141
|
return new TimebackClient({
|
|
57536
58142
|
...config2,
|
|
@@ -57543,15 +58149,15 @@ class TimebackClient {
|
|
|
57543
58149
|
if (explicit) {
|
|
57544
58150
|
return explicit;
|
|
57545
58151
|
}
|
|
57546
|
-
const envName = process.env[
|
|
57547
|
-
return
|
|
58152
|
+
const envName = process.env[ENV_VARS2.environment] === "staging" ? "staging" : "production";
|
|
58153
|
+
return TIMEBACK_API_URLS2[envName];
|
|
57548
58154
|
}
|
|
57549
58155
|
static resolveCaliperUrl(input, environment = "production") {
|
|
57550
58156
|
const explicit = (input || "").trim();
|
|
57551
58157
|
if (explicit) {
|
|
57552
58158
|
return explicit;
|
|
57553
58159
|
}
|
|
57554
|
-
return
|
|
58160
|
+
return CALIPER_API_URLS2[environment];
|
|
57555
58161
|
}
|
|
57556
58162
|
getBaseUrl() {
|
|
57557
58163
|
return this.baseUrl;
|
|
@@ -57672,14 +58278,14 @@ class TimebackClient {
|
|
|
57672
58278
|
await this._ensureAuthenticated();
|
|
57673
58279
|
return this.token;
|
|
57674
58280
|
}
|
|
57675
|
-
throw new TimebackAuthenticationError(`QTI credentials are required. Set ${
|
|
58281
|
+
throw new TimebackAuthenticationError(`QTI credentials are required. Set ${ENV_VARS2.qtiClientId}, ${ENV_VARS2.qtiClientSecret}, and ${ENV_VARS2.qtiAuthUrl}.`);
|
|
57676
58282
|
}
|
|
57677
58283
|
async ensureQtiAuthenticated() {
|
|
57678
58284
|
if (this.isQtiAuthenticated()) {
|
|
57679
58285
|
return;
|
|
57680
58286
|
}
|
|
57681
58287
|
if (!this.qtiCredentials) {
|
|
57682
|
-
throw new TimebackAuthenticationError(`QTI credentials are required. Set ${
|
|
58288
|
+
throw new TimebackAuthenticationError(`QTI credentials are required. Set ${ENV_VARS2.qtiClientId}, ${ENV_VARS2.qtiClientSecret}, and ${ENV_VARS2.qtiAuthUrl}.`);
|
|
57683
58289
|
}
|
|
57684
58290
|
try {
|
|
57685
58291
|
const tokenData = await getTimebackTokenResponse({
|
|
@@ -57691,11 +58297,11 @@ class TimebackClient {
|
|
|
57691
58297
|
this.setQtiToken(tokenData.access_token, tokenData.expires_in);
|
|
57692
58298
|
} catch (error) {
|
|
57693
58299
|
const errMsg = errorMessage(error);
|
|
57694
|
-
throw new TimebackAuthenticationError(`QTI authentication failed: ${errMsg}. Verify that ${
|
|
58300
|
+
throw new TimebackAuthenticationError(`QTI authentication failed: ${errMsg}. Verify that ${ENV_VARS2.qtiClientId}, ${ENV_VARS2.qtiClientSecret}, and ${ENV_VARS2.qtiAuthUrl} are correct.`);
|
|
57695
58301
|
}
|
|
57696
58302
|
}
|
|
57697
58303
|
canUseCoreCredentialsForQti() {
|
|
57698
|
-
return this.baseUrl.replace(/\/$/, "") ===
|
|
58304
|
+
return this.baseUrl.replace(/\/$/, "") === TIMEBACK_API_URLS2.production;
|
|
57699
58305
|
}
|
|
57700
58306
|
async resolveStudent(studentIdentifier, providedEmail) {
|
|
57701
58307
|
return this.studentResolver.resolve(studentIdentifier, providedEmail);
|
|
@@ -57716,8 +58322,8 @@ class TimebackClient {
|
|
|
57716
58322
|
await this._ensureAuthenticated();
|
|
57717
58323
|
const edubridgeEnrollments = await this.edubridge.enrollments.listByUser(studentId);
|
|
57718
58324
|
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(
|
|
58325
|
+
const grades = enrollment.course.grades ? enrollment.course.grades.map((g) => parseInt(g, 10)).filter(isTimebackGrade3) : null;
|
|
58326
|
+
const subjects = enrollment.course.subjects ? enrollment.course.subjects.filter(isTimebackSubject3) : null;
|
|
57721
58327
|
return {
|
|
57722
58328
|
sourcedId: enrollment.id,
|
|
57723
58329
|
title: enrollment.course.title,
|
|
@@ -57783,9 +58389,9 @@ class TimebackClient {
|
|
|
57783
58389
|
if (options?.include?.perCourse) {
|
|
57784
58390
|
const gradeStr = enrollment.course.grades?.[0];
|
|
57785
58391
|
const parsedGrade = gradeStr ? parseInt(gradeStr, 10) : 0;
|
|
57786
|
-
const grade =
|
|
58392
|
+
const grade = isTimebackGrade3(parsedGrade) ? parsedGrade : 0;
|
|
57787
58393
|
const subjectStr = enrollment.course.subjects?.[0];
|
|
57788
|
-
const subject = subjectStr &&
|
|
58394
|
+
const subject = subjectStr && isTimebackSubject3(subjectStr) ? subjectStr : "None";
|
|
57789
58395
|
courses.push({
|
|
57790
58396
|
grade,
|
|
57791
58397
|
subject,
|
|
@@ -57859,9 +58465,9 @@ class TimebackClient {
|
|
|
57859
58465
|
if (options?.include?.perCourse) {
|
|
57860
58466
|
const gradeStr = enrollment.course.grades?.[0];
|
|
57861
58467
|
const parsedGrade = gradeStr ? parseInt(gradeStr, 10) : 0;
|
|
57862
|
-
const grade =
|
|
58468
|
+
const grade = isTimebackGrade3(parsedGrade) ? parsedGrade : 0;
|
|
57863
58469
|
const subjectStr = enrollment.course.subjects?.[0];
|
|
57864
|
-
const subject = subjectStr &&
|
|
58470
|
+
const subject = subjectStr && isTimebackSubject3(subjectStr) ? subjectStr : "None";
|
|
57865
58471
|
courses.push({
|
|
57866
58472
|
grade,
|
|
57867
58473
|
subject,
|
|
@@ -57917,6 +58523,12 @@ class TimebackClient {
|
|
|
57917
58523
|
async cleanup(courseId) {
|
|
57918
58524
|
return deleteTimebackResources(this, courseId);
|
|
57919
58525
|
}
|
|
58526
|
+
async deactivateCourse(courseId) {
|
|
58527
|
+
return updateTimebackCourseStatus(this, courseId, "tobedeleted");
|
|
58528
|
+
}
|
|
58529
|
+
async reactivateCourse(courseId) {
|
|
58530
|
+
return updateTimebackCourseStatus(this, courseId, "active");
|
|
58531
|
+
}
|
|
57920
58532
|
}
|
|
57921
58533
|
var __defProp22;
|
|
57922
58534
|
var __export2 = (target, all) => {
|
|
@@ -57928,46 +58540,50 @@ var __export2 = (target, all) => {
|
|
|
57928
58540
|
set: (newValue) => all[name3] = () => newValue
|
|
57929
58541
|
});
|
|
57930
58542
|
};
|
|
57931
|
-
var
|
|
57932
|
-
var
|
|
58543
|
+
var __esm3 = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
58544
|
+
var TIMEBACK_API_URLS2;
|
|
57933
58545
|
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;
|
|
58546
|
+
var TIMEBACK_AUTH_URLS2;
|
|
58547
|
+
var CALIPER_API_URLS2;
|
|
58548
|
+
var ONEROSTER_ENDPOINTS2;
|
|
58549
|
+
var QTI_ENDPOINTS2;
|
|
58550
|
+
var CALIPER_ENDPOINTS2;
|
|
58551
|
+
var CALIPER_CONSTANTS2;
|
|
58552
|
+
var TIMEBACK_EVENT_TYPES2;
|
|
58553
|
+
var TIMEBACK_ACTIONS2;
|
|
58554
|
+
var TIMEBACK_TYPES2;
|
|
58555
|
+
var ACTIVITY_METRIC_TYPES2;
|
|
58556
|
+
var TIME_METRIC_TYPES2;
|
|
58557
|
+
var TIMEBACK_SUBJECTS3;
|
|
58558
|
+
var TIMEBACK_GRADE_LEVELS2;
|
|
58559
|
+
var TIMEBACK_GRADE_LEVEL_LABELS2;
|
|
58560
|
+
var CALIPER_SUBJECTS2;
|
|
58561
|
+
var ONEROSTER_STATUS2;
|
|
58562
|
+
var SCORE_STATUS2;
|
|
58563
|
+
var ENV_VARS2;
|
|
58564
|
+
var HTTP_DEFAULTS2;
|
|
58565
|
+
var AUTH_DEFAULTS2;
|
|
58566
|
+
var CACHE_DEFAULTS2;
|
|
58567
|
+
var CONFIG_DEFAULTS2;
|
|
58568
|
+
var PLAYCADEMY_DEFAULTS2;
|
|
58569
|
+
var RESOURCE_DEFAULTS2;
|
|
58570
|
+
var HTTP_STATUS2;
|
|
58571
|
+
var ERROR_NAMES2;
|
|
58572
|
+
var init_constants5;
|
|
57963
58573
|
var TimebackError;
|
|
57964
58574
|
var TimebackApiError;
|
|
57965
58575
|
var TimebackAuthenticationError;
|
|
57966
58576
|
var StudentNotFoundError;
|
|
57967
58577
|
var ConfigurationError;
|
|
58578
|
+
var ResourceAlreadyExistsError;
|
|
57968
58579
|
var ResourceNotFoundError;
|
|
57969
|
-
var
|
|
57970
|
-
var
|
|
58580
|
+
var init_errors4;
|
|
58581
|
+
var UUID_REGEX2;
|
|
58582
|
+
var init_ids;
|
|
58583
|
+
var exports_verify;
|
|
58584
|
+
var init_verify;
|
|
58585
|
+
var SUBJECT_VALUES2;
|
|
58586
|
+
var GRADE_VALUES2;
|
|
57971
58587
|
var TimebackAuthError;
|
|
57972
58588
|
var UUID_PATTERN;
|
|
57973
58589
|
var storage;
|
|
@@ -58000,320 +58616,7 @@ var init_dist2 = __esm(() => {
|
|
|
58000
58616
|
init_spans();
|
|
58001
58617
|
init_esm();
|
|
58002
58618
|
__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(() => {
|
|
58619
|
+
init_constants5 = __esm3(() => {
|
|
58317
58620
|
TIMEBACK_API_URLS2 = {
|
|
58318
58621
|
production: "https://api.alpha-1edtech.ai",
|
|
58319
58622
|
staging: "https://api.staging.alpha-1edtech.com"
|
|
@@ -58495,34 +58798,152 @@ var init_constants5 = __esm(() => {
|
|
|
58495
58798
|
timebackSdk: "TimebackSDKError"
|
|
58496
58799
|
};
|
|
58497
58800
|
});
|
|
58498
|
-
|
|
58801
|
+
init_errors4 = __esm3(() => {
|
|
58802
|
+
init_constants5();
|
|
58803
|
+
TimebackError = class TimebackError2 extends Error {
|
|
58804
|
+
constructor(message) {
|
|
58805
|
+
super(message);
|
|
58806
|
+
this.name = ERROR_NAMES2.timebackSdk;
|
|
58807
|
+
}
|
|
58808
|
+
};
|
|
58809
|
+
TimebackApiError = class TimebackApiError2 extends Error {
|
|
58810
|
+
status;
|
|
58811
|
+
details;
|
|
58812
|
+
constructor(status, message, details) {
|
|
58813
|
+
super(`${status} ${message}`);
|
|
58814
|
+
this.name = ERROR_NAMES2.timebackApi;
|
|
58815
|
+
this.status = status;
|
|
58816
|
+
this.details = details;
|
|
58817
|
+
Object.setPrototypeOf(this, TimebackApiError2.prototype);
|
|
58818
|
+
}
|
|
58819
|
+
};
|
|
58820
|
+
TimebackAuthenticationError = class TimebackAuthenticationError2 extends TimebackError {
|
|
58821
|
+
constructor(message) {
|
|
58822
|
+
super(message || "Authentication failed. Please verify TIMEBACK_CLIENT_ID and TIMEBACK_CLIENT_SECRET are set correctly.");
|
|
58823
|
+
this.name = "TimebackAuthenticationError";
|
|
58824
|
+
Object.setPrototypeOf(this, TimebackAuthenticationError2.prototype);
|
|
58825
|
+
}
|
|
58826
|
+
};
|
|
58827
|
+
StudentNotFoundError = class StudentNotFoundError2 extends TimebackError {
|
|
58828
|
+
identifier;
|
|
58829
|
+
identifierType;
|
|
58830
|
+
constructor(identifier, identifierType = "email") {
|
|
58831
|
+
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.`);
|
|
58832
|
+
this.name = "StudentNotFoundError";
|
|
58833
|
+
this.identifier = identifier;
|
|
58834
|
+
this.identifierType = identifierType;
|
|
58835
|
+
Object.setPrototypeOf(this, StudentNotFoundError2.prototype);
|
|
58836
|
+
}
|
|
58837
|
+
};
|
|
58838
|
+
ConfigurationError = class ConfigurationError2 extends TimebackError {
|
|
58839
|
+
field;
|
|
58840
|
+
constructor(field, message) {
|
|
58841
|
+
super(message || `Missing required configuration: ${field}. Please check your timeback.config.js file.`);
|
|
58842
|
+
this.name = "ConfigurationError";
|
|
58843
|
+
this.field = field;
|
|
58844
|
+
Object.setPrototypeOf(this, ConfigurationError2.prototype);
|
|
58845
|
+
}
|
|
58846
|
+
};
|
|
58847
|
+
ResourceAlreadyExistsError = class ResourceAlreadyExistsError2 extends TimebackError {
|
|
58848
|
+
resourceType;
|
|
58849
|
+
sourcedId;
|
|
58850
|
+
originalError;
|
|
58851
|
+
constructor(resourceType, sourcedId, originalError) {
|
|
58852
|
+
super(`${resourceType} with ID '${sourcedId}' already exists`);
|
|
58853
|
+
this.name = "ResourceAlreadyExistsError";
|
|
58854
|
+
this.resourceType = resourceType;
|
|
58855
|
+
this.sourcedId = sourcedId;
|
|
58856
|
+
this.originalError = originalError;
|
|
58857
|
+
Object.setPrototypeOf(this, ResourceAlreadyExistsError2.prototype);
|
|
58858
|
+
}
|
|
58859
|
+
};
|
|
58860
|
+
ResourceNotFoundError = class ResourceNotFoundError2 extends TimebackError {
|
|
58861
|
+
resourceType;
|
|
58862
|
+
sourcedId;
|
|
58863
|
+
constructor(resourceType, sourcedId) {
|
|
58864
|
+
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`;
|
|
58865
|
+
super(message);
|
|
58866
|
+
this.name = "ResourceNotFoundError";
|
|
58867
|
+
this.resourceType = resourceType;
|
|
58868
|
+
this.sourcedId = sourcedId;
|
|
58869
|
+
Object.setPrototypeOf(this, ResourceNotFoundError2.prototype);
|
|
58870
|
+
}
|
|
58871
|
+
};
|
|
58872
|
+
});
|
|
58873
|
+
init_ids = __esm3(() => {
|
|
58874
|
+
init_errors4();
|
|
58875
|
+
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;
|
|
58876
|
+
});
|
|
58877
|
+
exports_verify = {};
|
|
58878
|
+
__export2(exports_verify, {
|
|
58879
|
+
verifyTimebackResources: () => verifyTimebackResources,
|
|
58880
|
+
fetchTimebackConfig: () => fetchTimebackConfig
|
|
58881
|
+
});
|
|
58882
|
+
init_verify = __esm3(() => {
|
|
58883
|
+
init_constants5();
|
|
58884
|
+
init_ids();
|
|
58885
|
+
});
|
|
58886
|
+
init_constants5();
|
|
58887
|
+
SUBJECT_VALUES2 = TIMEBACK_SUBJECTS3;
|
|
58888
|
+
GRADE_VALUES2 = TIMEBACK_GRADE_LEVELS2;
|
|
58889
|
+
init_ids();
|
|
58890
|
+
init_ids();
|
|
58891
|
+
init_verify();
|
|
58892
|
+
init_ids();
|
|
58893
|
+
init_constants5();
|
|
58894
|
+
init_errors4();
|
|
58895
|
+
init_constants5();
|
|
58896
|
+
if (process.env.DEBUG === "true") {
|
|
58897
|
+
process.env.TERM = "dumb";
|
|
58898
|
+
}
|
|
58899
|
+
TimebackAuthError = class TimebackAuthError2 extends Error {
|
|
58900
|
+
statusCode;
|
|
58901
|
+
constructor(message, statusCode) {
|
|
58902
|
+
super(message);
|
|
58903
|
+
this.name = ERROR_NAMES2.timebackAuth;
|
|
58904
|
+
this.statusCode = statusCode;
|
|
58905
|
+
}
|
|
58906
|
+
};
|
|
58907
|
+
init_ids();
|
|
58908
|
+
init_constants5();
|
|
58909
|
+
init_errors4();
|
|
58910
|
+
UUID_PATTERN = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi;
|
|
58911
|
+
storage = new AsyncLocalStorage;
|
|
58912
|
+
init_constants5();
|
|
58913
|
+
init_constants5();
|
|
58914
|
+
init_errors4();
|
|
58915
|
+
init_constants5();
|
|
58916
|
+
init_constants5();
|
|
58917
|
+
init_constants5();
|
|
58918
|
+
init_constants5();
|
|
58919
|
+
init_constants5();
|
|
58920
|
+
init_ids();
|
|
58921
|
+
init_ids();
|
|
58922
|
+
init_errors4();
|
|
58923
|
+
init_ids();
|
|
58924
|
+
init_errors4();
|
|
58925
|
+
EmailSchema = exports_external.string().email();
|
|
58926
|
+
StudentSourcedIdSchema = exports_external.string().min(1, {
|
|
58927
|
+
message: "Student sourcedId must be a non-empty string"
|
|
58928
|
+
});
|
|
58929
|
+
StudentIdentifierSchema = exports_external.union([EmailSchema, StudentSourcedIdSchema]);
|
|
58930
|
+
init_ids();
|
|
58499
58931
|
});
|
|
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;
|
|
58932
|
+
function deriveTimebackCourseLevelFromGrade(grade) {
|
|
58933
|
+
if (grade === 13) {
|
|
58934
|
+
return TIMEBACK_COURSE_DEFAULTS.level.ap;
|
|
58512
58935
|
}
|
|
58513
|
-
if (
|
|
58514
|
-
return
|
|
58936
|
+
if (grade <= 5) {
|
|
58937
|
+
return TIMEBACK_COURSE_DEFAULTS.level.elementary;
|
|
58515
58938
|
}
|
|
58516
|
-
|
|
58517
|
-
|
|
58518
|
-
|
|
58519
|
-
return
|
|
58520
|
-
}
|
|
58521
|
-
function isTimebackGrade3(value) {
|
|
58522
|
-
return typeof value === "number" && Number.isInteger(value) && GRADE_VALUES2.includes(value);
|
|
58939
|
+
if (grade <= 8) {
|
|
58940
|
+
return TIMEBACK_COURSE_DEFAULTS.level.middle;
|
|
58941
|
+
}
|
|
58942
|
+
return TIMEBACK_COURSE_DEFAULTS.level.high;
|
|
58523
58943
|
}
|
|
58524
58944
|
var __esm4 = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
58525
58945
|
var TIMEBACK_API_URLS3;
|
|
58946
|
+
var QTI_API_URL2 = "https://qti.alpha-1edtech.ai/api";
|
|
58526
58947
|
var TIMEBACK_AUTH_URLS3;
|
|
58527
58948
|
var CALIPER_API_URLS3;
|
|
58528
58949
|
var ONEROSTER_ENDPOINTS3;
|
|
@@ -58550,9 +58971,7 @@ var RESOURCE_DEFAULTS3;
|
|
|
58550
58971
|
var HTTP_STATUS3;
|
|
58551
58972
|
var ERROR_NAMES3;
|
|
58552
58973
|
var init_constants7;
|
|
58553
|
-
var
|
|
58554
|
-
var GRADE_VALUES2;
|
|
58555
|
-
var init_types3 = __esm(() => {
|
|
58974
|
+
var init_constants6 = __esm(() => {
|
|
58556
58975
|
init_src();
|
|
58557
58976
|
init_constants7 = __esm4(() => {
|
|
58558
58977
|
TIMEBACK_API_URLS3 = {
|
|
@@ -58737,8 +59156,6 @@ var init_types3 = __esm(() => {
|
|
|
58737
59156
|
};
|
|
58738
59157
|
});
|
|
58739
59158
|
init_constants7();
|
|
58740
|
-
SUBJECT_VALUES2 = TIMEBACK_SUBJECTS4;
|
|
58741
|
-
GRADE_VALUES2 = TIMEBACK_GRADE_LEVELS3;
|
|
58742
59159
|
});
|
|
58743
59160
|
function deriveSourcedIds2(courseId) {
|
|
58744
59161
|
return {
|
|
@@ -58787,6 +59204,16 @@ var RESOURCE_DEFAULTS4;
|
|
|
58787
59204
|
var HTTP_STATUS4;
|
|
58788
59205
|
var ERROR_NAMES4;
|
|
58789
59206
|
var init_constants8;
|
|
59207
|
+
var TimebackError2;
|
|
59208
|
+
var TimebackApiError2;
|
|
59209
|
+
var TimebackAuthenticationError2;
|
|
59210
|
+
var StudentNotFoundError2;
|
|
59211
|
+
var ConfigurationError2;
|
|
59212
|
+
var ResourceAlreadyExistsError2;
|
|
59213
|
+
var ResourceNotFoundError2;
|
|
59214
|
+
var init_errors5;
|
|
59215
|
+
var UUID_REGEX3;
|
|
59216
|
+
var init_ids2;
|
|
58790
59217
|
var init_utils6 = __esm(() => {
|
|
58791
59218
|
init_src();
|
|
58792
59219
|
init_constants8 = __esm5(() => {
|
|
@@ -58971,6 +59398,82 @@ var init_utils6 = __esm(() => {
|
|
|
58971
59398
|
timebackSdk: "TimebackSDKError"
|
|
58972
59399
|
};
|
|
58973
59400
|
});
|
|
59401
|
+
init_errors5 = __esm5(() => {
|
|
59402
|
+
init_constants8();
|
|
59403
|
+
TimebackError2 = class TimebackError3 extends Error {
|
|
59404
|
+
constructor(message) {
|
|
59405
|
+
super(message);
|
|
59406
|
+
this.name = ERROR_NAMES4.timebackSdk;
|
|
59407
|
+
}
|
|
59408
|
+
};
|
|
59409
|
+
TimebackApiError2 = class TimebackApiError3 extends Error {
|
|
59410
|
+
status;
|
|
59411
|
+
details;
|
|
59412
|
+
constructor(status, message, details) {
|
|
59413
|
+
super(`${status} ${message}`);
|
|
59414
|
+
this.name = ERROR_NAMES4.timebackApi;
|
|
59415
|
+
this.status = status;
|
|
59416
|
+
this.details = details;
|
|
59417
|
+
Object.setPrototypeOf(this, TimebackApiError3.prototype);
|
|
59418
|
+
}
|
|
59419
|
+
};
|
|
59420
|
+
TimebackAuthenticationError2 = class TimebackAuthenticationError3 extends TimebackError2 {
|
|
59421
|
+
constructor(message) {
|
|
59422
|
+
super(message || "Authentication failed. Please verify TIMEBACK_CLIENT_ID and TIMEBACK_CLIENT_SECRET are set correctly.");
|
|
59423
|
+
this.name = "TimebackAuthenticationError";
|
|
59424
|
+
Object.setPrototypeOf(this, TimebackAuthenticationError3.prototype);
|
|
59425
|
+
}
|
|
59426
|
+
};
|
|
59427
|
+
StudentNotFoundError2 = class StudentNotFoundError3 extends TimebackError2 {
|
|
59428
|
+
identifier;
|
|
59429
|
+
identifierType;
|
|
59430
|
+
constructor(identifier, identifierType = "email") {
|
|
59431
|
+
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.`);
|
|
59432
|
+
this.name = "StudentNotFoundError";
|
|
59433
|
+
this.identifier = identifier;
|
|
59434
|
+
this.identifierType = identifierType;
|
|
59435
|
+
Object.setPrototypeOf(this, StudentNotFoundError3.prototype);
|
|
59436
|
+
}
|
|
59437
|
+
};
|
|
59438
|
+
ConfigurationError2 = class ConfigurationError3 extends TimebackError2 {
|
|
59439
|
+
field;
|
|
59440
|
+
constructor(field, message) {
|
|
59441
|
+
super(message || `Missing required configuration: ${field}. Please check your timeback.config.js file.`);
|
|
59442
|
+
this.name = "ConfigurationError";
|
|
59443
|
+
this.field = field;
|
|
59444
|
+
Object.setPrototypeOf(this, ConfigurationError3.prototype);
|
|
59445
|
+
}
|
|
59446
|
+
};
|
|
59447
|
+
ResourceAlreadyExistsError2 = class ResourceAlreadyExistsError3 extends TimebackError2 {
|
|
59448
|
+
resourceType;
|
|
59449
|
+
sourcedId;
|
|
59450
|
+
originalError;
|
|
59451
|
+
constructor(resourceType, sourcedId, originalError) {
|
|
59452
|
+
super(`${resourceType} with ID '${sourcedId}' already exists`);
|
|
59453
|
+
this.name = "ResourceAlreadyExistsError";
|
|
59454
|
+
this.resourceType = resourceType;
|
|
59455
|
+
this.sourcedId = sourcedId;
|
|
59456
|
+
this.originalError = originalError;
|
|
59457
|
+
Object.setPrototypeOf(this, ResourceAlreadyExistsError3.prototype);
|
|
59458
|
+
}
|
|
59459
|
+
};
|
|
59460
|
+
ResourceNotFoundError2 = class ResourceNotFoundError3 extends TimebackError2 {
|
|
59461
|
+
resourceType;
|
|
59462
|
+
sourcedId;
|
|
59463
|
+
constructor(resourceType, sourcedId) {
|
|
59464
|
+
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`;
|
|
59465
|
+
super(message);
|
|
59466
|
+
this.name = "ResourceNotFoundError";
|
|
59467
|
+
this.resourceType = resourceType;
|
|
59468
|
+
this.sourcedId = sourcedId;
|
|
59469
|
+
Object.setPrototypeOf(this, ResourceNotFoundError3.prototype);
|
|
59470
|
+
}
|
|
59471
|
+
};
|
|
59472
|
+
});
|
|
59473
|
+
init_ids2 = __esm5(() => {
|
|
59474
|
+
init_errors5();
|
|
59475
|
+
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;
|
|
59476
|
+
});
|
|
58974
59477
|
init_constants8();
|
|
58975
59478
|
init_constants8();
|
|
58976
59479
|
if (process.env.DEBUG === "true") {
|
|
@@ -59130,331 +59633,6 @@ function compareEnrollmentsByRecency(a, b) {
|
|
|
59130
59633
|
var init_timeback_admin_util = __esm(() => {
|
|
59131
59634
|
init_errors();
|
|
59132
59635
|
});
|
|
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
59636
|
function parseDateInputParts(value) {
|
|
59459
59637
|
const parts2 = value.split("-");
|
|
59460
59638
|
return {
|
|
@@ -59823,7 +60001,7 @@ function isAllowedGradeLevelTestType(value) {
|
|
|
59823
60001
|
}
|
|
59824
60002
|
function isQualifyingGradeLevelTestResult(result, options) {
|
|
59825
60003
|
const metadata2 = metadataOf(result);
|
|
59826
|
-
return sourceIdFromRef(result.student) === options.studentId && normalizeText(result.status) === "active" && result.scoreStatus ===
|
|
60004
|
+
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
60005
|
}
|
|
59828
60006
|
function mapGradeLevelTestSummary(result, options) {
|
|
59829
60007
|
if (!isQualifyingGradeLevelTestResult(result, options)) {
|
|
@@ -60083,7 +60261,7 @@ function latestAssessmentResultsByLineItem(results) {
|
|
|
60083
60261
|
const latestResultsByLineItem = new Map;
|
|
60084
60262
|
for (const result of results) {
|
|
60085
60263
|
const lineItemId = sourceIdFromRef(result.assessmentLineItem);
|
|
60086
|
-
if (lineItemId && !latestResultsByLineItem.has(lineItemId) && result.scoreStatus ===
|
|
60264
|
+
if (lineItemId && !latestResultsByLineItem.has(lineItemId) && result.scoreStatus === SCORE_STATUS3.fullyGraded) {
|
|
60087
60265
|
latestResultsByLineItem.set(lineItemId, result);
|
|
60088
60266
|
}
|
|
60089
60267
|
}
|
|
@@ -60184,7 +60362,7 @@ function qtiIdCandidates(parentLineItem, resource) {
|
|
|
60184
60362
|
var GRADE_LEVEL_TEST_TYPES;
|
|
60185
60363
|
var naturalTitleSort;
|
|
60186
60364
|
var init_timeback_grade_level_results_util = __esm(() => {
|
|
60187
|
-
|
|
60365
|
+
init_constants6();
|
|
60188
60366
|
init_utils6();
|
|
60189
60367
|
GRADE_LEVEL_TEST_TYPES = [
|
|
60190
60368
|
"placement",
|
|
@@ -60207,18 +60385,18 @@ async function upsertMasteryCompletionEntry(params) {
|
|
|
60207
60385
|
await client.oneroster.assessmentLineItems.findOrCreate(lineItemId, {
|
|
60208
60386
|
sourcedId: lineItemId,
|
|
60209
60387
|
title: "Mastery Completion",
|
|
60210
|
-
status:
|
|
60388
|
+
status: ONEROSTER_STATUS3.active,
|
|
60211
60389
|
course: { sourcedId: ids.course },
|
|
60212
60390
|
...ids.componentResource ? { componentResource: { sourcedId: ids.componentResource } } : {}
|
|
60213
60391
|
});
|
|
60214
60392
|
await client.oneroster.assessmentResults.upsert(resultId, {
|
|
60215
60393
|
sourcedId: resultId,
|
|
60216
|
-
status:
|
|
60394
|
+
status: ONEROSTER_STATUS3.active,
|
|
60217
60395
|
assessmentLineItem: { sourcedId: lineItemId },
|
|
60218
60396
|
student: { sourcedId: studentId },
|
|
60219
60397
|
score: 100,
|
|
60220
60398
|
scoreDate: new Date().toISOString(),
|
|
60221
|
-
scoreStatus:
|
|
60399
|
+
scoreStatus: SCORE_STATUS3.fullyGraded,
|
|
60222
60400
|
inProgress: "false",
|
|
60223
60401
|
metadata: {
|
|
60224
60402
|
isMasteryCompletion: true,
|
|
@@ -60230,12 +60408,12 @@ async function upsertMasteryCompletionEntry(params) {
|
|
|
60230
60408
|
try {
|
|
60231
60409
|
await client.oneroster.assessmentResults.upsert(resultId, {
|
|
60232
60410
|
sourcedId: resultId,
|
|
60233
|
-
status:
|
|
60411
|
+
status: ONEROSTER_STATUS3.active,
|
|
60234
60412
|
assessmentLineItem: { sourcedId: lineItemId },
|
|
60235
60413
|
student: { sourcedId: studentId },
|
|
60236
60414
|
score: 0,
|
|
60237
60415
|
scoreDate: new Date().toISOString(),
|
|
60238
|
-
scoreStatus:
|
|
60416
|
+
scoreStatus: SCORE_STATUS3.notSubmitted,
|
|
60239
60417
|
inProgress: "true",
|
|
60240
60418
|
metadata: {
|
|
60241
60419
|
isMasteryCompletion: true,
|
|
@@ -60247,7 +60425,7 @@ async function upsertMasteryCompletionEntry(params) {
|
|
|
60247
60425
|
}
|
|
60248
60426
|
}
|
|
60249
60427
|
var init_timeback_mastery_completion_util = __esm(() => {
|
|
60250
|
-
|
|
60428
|
+
init_constants6();
|
|
60251
60429
|
init_utils6();
|
|
60252
60430
|
});
|
|
60253
60431
|
|
|
@@ -60275,7 +60453,7 @@ class TimebackAdminService {
|
|
|
60275
60453
|
this.deps = deps;
|
|
60276
60454
|
}
|
|
60277
60455
|
getGradeLevelTestCourseScope(integration) {
|
|
60278
|
-
if (!
|
|
60456
|
+
if (!isTimebackSubject2(integration.subject) || !isTimebackGrade2(integration.grade)) {
|
|
60279
60457
|
throw new ValidationError("Timeback integration has invalid grade or subject");
|
|
60280
60458
|
}
|
|
60281
60459
|
return {
|
|
@@ -60354,7 +60532,7 @@ class TimebackAdminService {
|
|
|
60354
60532
|
await this.deps.validateDeveloperAccess(user, gameId);
|
|
60355
60533
|
}
|
|
60356
60534
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
60357
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
60535
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus())
|
|
60358
60536
|
});
|
|
60359
60537
|
if (!integration) {
|
|
60360
60538
|
throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
|
|
@@ -60452,7 +60630,7 @@ class TimebackAdminService {
|
|
|
60452
60630
|
const ids = deriveSourcedIds2(courseId);
|
|
60453
60631
|
const resource = await client.oneroster.resources.get(ids.resource);
|
|
60454
60632
|
const playcademyMetadata = resource.metadata?.playcademy;
|
|
60455
|
-
if (!
|
|
60633
|
+
if (!isPlaycademyResourceMetadata(playcademyMetadata)) {
|
|
60456
60634
|
return;
|
|
60457
60635
|
}
|
|
60458
60636
|
return playcademyMetadata?.mastery?.masterableUnits;
|
|
@@ -60856,7 +61034,7 @@ class TimebackAdminService {
|
|
|
60856
61034
|
"app.timeback.include_inactive": options?.includeInactive ?? false
|
|
60857
61035
|
});
|
|
60858
61036
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
60859
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
61037
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus())
|
|
60860
61038
|
});
|
|
60861
61039
|
if (!integration) {
|
|
60862
61040
|
throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
|
|
@@ -60934,7 +61112,7 @@ class TimebackAdminService {
|
|
|
60934
61112
|
columns: { id: true }
|
|
60935
61113
|
}),
|
|
60936
61114
|
this.deps.db.query.gameTimebackIntegrations.findMany({
|
|
60937
|
-
where: eq(gameTimebackIntegrations.gameId, gameId)
|
|
61115
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), isActiveGameTimebackIntegrationStatus())
|
|
60938
61116
|
}),
|
|
60939
61117
|
this.deps.db.query.games.findFirst({
|
|
60940
61118
|
where: eq(games.id, gameId),
|
|
@@ -61037,7 +61215,7 @@ class TimebackAdminService {
|
|
|
61037
61215
|
"app.timeback.course_id": courseId
|
|
61038
61216
|
});
|
|
61039
61217
|
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)
|
|
61218
|
+
where: courseId ? and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus()) : and(eq(gameTimebackIntegrations.gameId, gameId), isActiveGameTimebackIntegrationStatus())
|
|
61041
61219
|
});
|
|
61042
61220
|
if (integrations.length === 0) {
|
|
61043
61221
|
throw new NotFoundError("Timeback integration", gameId);
|
|
@@ -61127,7 +61305,7 @@ class TimebackAdminService {
|
|
|
61127
61305
|
});
|
|
61128
61306
|
const [integration, gameSource] = await Promise.all([
|
|
61129
61307
|
this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
61130
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
61308
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus())
|
|
61131
61309
|
}),
|
|
61132
61310
|
this.getGameActivitySource(gameId)
|
|
61133
61311
|
]);
|
|
@@ -61231,7 +61409,7 @@ class TimebackAdminService {
|
|
|
61231
61409
|
"app.timeback.course_id": courseId
|
|
61232
61410
|
});
|
|
61233
61411
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
61234
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
61412
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus())
|
|
61235
61413
|
});
|
|
61236
61414
|
if (!integration) {
|
|
61237
61415
|
throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
|
|
@@ -61286,7 +61464,7 @@ class TimebackAdminService {
|
|
|
61286
61464
|
"app.timeback.assessment_result_id": resultId
|
|
61287
61465
|
});
|
|
61288
61466
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
61289
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
61467
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus())
|
|
61290
61468
|
});
|
|
61291
61469
|
if (!integration) {
|
|
61292
61470
|
throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
|
|
@@ -61364,7 +61542,7 @@ class TimebackAdminService {
|
|
|
61364
61542
|
});
|
|
61365
61543
|
const [integration, gameSource, roster] = await Promise.all([
|
|
61366
61544
|
this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
61367
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
61545
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus())
|
|
61368
61546
|
}),
|
|
61369
61547
|
this.getGameActivitySource(gameId),
|
|
61370
61548
|
client.oneroster.enrollments.listByCourse(courseId, {
|
|
@@ -61485,7 +61663,7 @@ class TimebackAdminService {
|
|
|
61485
61663
|
});
|
|
61486
61664
|
const [integration, gameSource] = await Promise.all([
|
|
61487
61665
|
this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
61488
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
61666
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus())
|
|
61489
61667
|
}),
|
|
61490
61668
|
this.getGameActivitySource(gameId)
|
|
61491
61669
|
]);
|
|
@@ -61569,7 +61747,7 @@ class TimebackAdminService {
|
|
|
61569
61747
|
});
|
|
61570
61748
|
const [integration, gameSource] = await Promise.all([
|
|
61571
61749
|
this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
61572
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
61750
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus())
|
|
61573
61751
|
}),
|
|
61574
61752
|
this.getGameActivitySource(gameId)
|
|
61575
61753
|
]);
|
|
@@ -61798,7 +61976,7 @@ class TimebackAdminService {
|
|
|
61798
61976
|
"app.timeback.course_id": courseId
|
|
61799
61977
|
});
|
|
61800
61978
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
61801
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
61979
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus())
|
|
61802
61980
|
});
|
|
61803
61981
|
if (!integration) {
|
|
61804
61982
|
throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
|
|
@@ -61869,7 +62047,7 @@ class TimebackAdminService {
|
|
|
61869
62047
|
"app.timeback.enrollment.operation": "enroll"
|
|
61870
62048
|
});
|
|
61871
62049
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
61872
|
-
where: and(eq(gameTimebackIntegrations.gameId, data.gameId), eq(gameTimebackIntegrations.courseId, data.courseId))
|
|
62050
|
+
where: and(eq(gameTimebackIntegrations.gameId, data.gameId), eq(gameTimebackIntegrations.courseId, data.courseId), isActiveGameTimebackIntegrationStatus())
|
|
61873
62051
|
});
|
|
61874
62052
|
if (!integration) {
|
|
61875
62053
|
throw new NotFoundError("Timeback integration", `${data.gameId}:${data.courseId}`);
|
|
@@ -61909,7 +62087,7 @@ class TimebackAdminService {
|
|
|
61909
62087
|
"app.timeback.enrollment.operation": "unenroll"
|
|
61910
62088
|
});
|
|
61911
62089
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
61912
|
-
where: and(eq(gameTimebackIntegrations.gameId, data.gameId), eq(gameTimebackIntegrations.courseId, data.courseId))
|
|
62090
|
+
where: and(eq(gameTimebackIntegrations.gameId, data.gameId), eq(gameTimebackIntegrations.courseId, data.courseId), isActiveGameTimebackIntegrationStatus())
|
|
61913
62091
|
});
|
|
61914
62092
|
if (!integration) {
|
|
61915
62093
|
throw new NotFoundError("Timeback integration", `${data.gameId}:${data.courseId}`);
|
|
@@ -61930,7 +62108,7 @@ class TimebackAdminService {
|
|
|
61930
62108
|
"app.timeback.enrollment.operation": "reactivate"
|
|
61931
62109
|
});
|
|
61932
62110
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
61933
|
-
where: and(eq(gameTimebackIntegrations.gameId, data.gameId), eq(gameTimebackIntegrations.courseId, data.courseId))
|
|
62111
|
+
where: and(eq(gameTimebackIntegrations.gameId, data.gameId), eq(gameTimebackIntegrations.courseId, data.courseId), isActiveGameTimebackIntegrationStatus())
|
|
61934
62112
|
});
|
|
61935
62113
|
if (!integration) {
|
|
61936
62114
|
throw new NotFoundError("Timeback integration", `${data.gameId}:${data.courseId}`);
|
|
@@ -61976,7 +62154,7 @@ class TimebackAdminService {
|
|
|
61976
62154
|
const resultId = `${lineItemId}:${studentId}:completion`;
|
|
61977
62155
|
try {
|
|
61978
62156
|
const result = await client.oneroster.assessmentResults.get(resultId);
|
|
61979
|
-
if (result.scoreStatus ===
|
|
62157
|
+
if (result.scoreStatus === SCORE_STATUS3.fullyGraded) {
|
|
61980
62158
|
return "complete";
|
|
61981
62159
|
}
|
|
61982
62160
|
return "incomplete";
|
|
@@ -62012,12 +62190,13 @@ class TimebackAdminService {
|
|
|
62012
62190
|
var init_timeback_admin_service = __esm(() => {
|
|
62013
62191
|
init_drizzle_orm();
|
|
62014
62192
|
init_src();
|
|
62193
|
+
init_helpers_index();
|
|
62015
62194
|
init_schemas_index();
|
|
62016
62195
|
init_tables_index();
|
|
62017
62196
|
init_spans();
|
|
62018
62197
|
init_dist2();
|
|
62019
|
-
|
|
62020
|
-
|
|
62198
|
+
init_constants6();
|
|
62199
|
+
init_types2();
|
|
62021
62200
|
init_utils6();
|
|
62022
62201
|
init_src4();
|
|
62023
62202
|
init_timeback3();
|
|
@@ -62059,8 +62238,15 @@ var RESOURCE_DEFAULTS5;
|
|
|
62059
62238
|
var HTTP_STATUS5;
|
|
62060
62239
|
var ERROR_NAMES5;
|
|
62061
62240
|
var init_constants9;
|
|
62062
|
-
var
|
|
62063
|
-
var
|
|
62241
|
+
var TimebackError3;
|
|
62242
|
+
var TimebackApiError3;
|
|
62243
|
+
var TimebackAuthenticationError3;
|
|
62244
|
+
var StudentNotFoundError3;
|
|
62245
|
+
var ConfigurationError3;
|
|
62246
|
+
var ResourceAlreadyExistsError3;
|
|
62247
|
+
var ResourceNotFoundError3;
|
|
62248
|
+
var init_errors7;
|
|
62249
|
+
var init_errors6 = __esm(() => {
|
|
62064
62250
|
init_src();
|
|
62065
62251
|
init_constants9 = __esm6(() => {
|
|
62066
62252
|
TIMEBACK_API_URLS5 = {
|
|
@@ -62244,18 +62430,79 @@ var init_errors4 = __esm(() => {
|
|
|
62244
62430
|
timebackSdk: "TimebackSDKError"
|
|
62245
62431
|
};
|
|
62246
62432
|
});
|
|
62247
|
-
|
|
62248
|
-
|
|
62249
|
-
|
|
62250
|
-
|
|
62251
|
-
|
|
62252
|
-
|
|
62253
|
-
|
|
62254
|
-
|
|
62255
|
-
|
|
62256
|
-
|
|
62257
|
-
|
|
62258
|
-
|
|
62433
|
+
init_errors7 = __esm6(() => {
|
|
62434
|
+
init_constants9();
|
|
62435
|
+
TimebackError3 = class TimebackError4 extends Error {
|
|
62436
|
+
constructor(message) {
|
|
62437
|
+
super(message);
|
|
62438
|
+
this.name = ERROR_NAMES5.timebackSdk;
|
|
62439
|
+
}
|
|
62440
|
+
};
|
|
62441
|
+
TimebackApiError3 = class TimebackApiError4 extends Error {
|
|
62442
|
+
status;
|
|
62443
|
+
details;
|
|
62444
|
+
constructor(status, message, details) {
|
|
62445
|
+
super(`${status} ${message}`);
|
|
62446
|
+
this.name = ERROR_NAMES5.timebackApi;
|
|
62447
|
+
this.status = status;
|
|
62448
|
+
this.details = details;
|
|
62449
|
+
Object.setPrototypeOf(this, TimebackApiError4.prototype);
|
|
62450
|
+
}
|
|
62451
|
+
};
|
|
62452
|
+
TimebackAuthenticationError3 = class TimebackAuthenticationError4 extends TimebackError3 {
|
|
62453
|
+
constructor(message) {
|
|
62454
|
+
super(message || "Authentication failed. Please verify TIMEBACK_CLIENT_ID and TIMEBACK_CLIENT_SECRET are set correctly.");
|
|
62455
|
+
this.name = "TimebackAuthenticationError";
|
|
62456
|
+
Object.setPrototypeOf(this, TimebackAuthenticationError4.prototype);
|
|
62457
|
+
}
|
|
62458
|
+
};
|
|
62459
|
+
StudentNotFoundError3 = class StudentNotFoundError4 extends TimebackError3 {
|
|
62460
|
+
identifier;
|
|
62461
|
+
identifierType;
|
|
62462
|
+
constructor(identifier, identifierType = "email") {
|
|
62463
|
+
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.`);
|
|
62464
|
+
this.name = "StudentNotFoundError";
|
|
62465
|
+
this.identifier = identifier;
|
|
62466
|
+
this.identifierType = identifierType;
|
|
62467
|
+
Object.setPrototypeOf(this, StudentNotFoundError4.prototype);
|
|
62468
|
+
}
|
|
62469
|
+
};
|
|
62470
|
+
ConfigurationError3 = class ConfigurationError4 extends TimebackError3 {
|
|
62471
|
+
field;
|
|
62472
|
+
constructor(field, message) {
|
|
62473
|
+
super(message || `Missing required configuration: ${field}. Please check your timeback.config.js file.`);
|
|
62474
|
+
this.name = "ConfigurationError";
|
|
62475
|
+
this.field = field;
|
|
62476
|
+
Object.setPrototypeOf(this, ConfigurationError4.prototype);
|
|
62477
|
+
}
|
|
62478
|
+
};
|
|
62479
|
+
ResourceAlreadyExistsError3 = class ResourceAlreadyExistsError4 extends TimebackError3 {
|
|
62480
|
+
resourceType;
|
|
62481
|
+
sourcedId;
|
|
62482
|
+
originalError;
|
|
62483
|
+
constructor(resourceType, sourcedId, originalError) {
|
|
62484
|
+
super(`${resourceType} with ID '${sourcedId}' already exists`);
|
|
62485
|
+
this.name = "ResourceAlreadyExistsError";
|
|
62486
|
+
this.resourceType = resourceType;
|
|
62487
|
+
this.sourcedId = sourcedId;
|
|
62488
|
+
this.originalError = originalError;
|
|
62489
|
+
Object.setPrototypeOf(this, ResourceAlreadyExistsError4.prototype);
|
|
62490
|
+
}
|
|
62491
|
+
};
|
|
62492
|
+
ResourceNotFoundError3 = class ResourceNotFoundError4 extends TimebackError3 {
|
|
62493
|
+
resourceType;
|
|
62494
|
+
sourcedId;
|
|
62495
|
+
constructor(resourceType, sourcedId) {
|
|
62496
|
+
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`;
|
|
62497
|
+
super(message);
|
|
62498
|
+
this.name = "ResourceNotFoundError";
|
|
62499
|
+
this.resourceType = resourceType;
|
|
62500
|
+
this.sourcedId = sourcedId;
|
|
62501
|
+
Object.setPrototypeOf(this, ResourceNotFoundError4.prototype);
|
|
62502
|
+
}
|
|
62503
|
+
};
|
|
62504
|
+
});
|
|
62505
|
+
init_errors7();
|
|
62259
62506
|
});
|
|
62260
62507
|
|
|
62261
62508
|
class TimebackAssessmentsService {
|
|
@@ -62266,7 +62513,7 @@ class TimebackAssessmentsService {
|
|
|
62266
62513
|
async resolveIntegrationId(gameId, courseId, user) {
|
|
62267
62514
|
await this.deps.validateGameManagementAccess(user, gameId);
|
|
62268
62515
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
62269
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
62516
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus())
|
|
62270
62517
|
});
|
|
62271
62518
|
if (!integration) {
|
|
62272
62519
|
throw new NotFoundError(`No Timeback integration found for game ${gameId} course ${courseId}`);
|
|
@@ -62359,7 +62606,7 @@ class TimebackAssessmentsService {
|
|
|
62359
62606
|
]
|
|
62360
62607
|
});
|
|
62361
62608
|
} catch (error) {
|
|
62362
|
-
if (error instanceof
|
|
62609
|
+
if (error instanceof TimebackApiError3 && error.status === 409) {} else {
|
|
62363
62610
|
throw error;
|
|
62364
62611
|
}
|
|
62365
62612
|
}
|
|
@@ -62659,7 +62906,7 @@ class TimebackAssessmentsService {
|
|
|
62659
62906
|
}
|
|
62660
62907
|
async requireIntegration(integrationId) {
|
|
62661
62908
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
62662
|
-
where: eq(gameTimebackIntegrations.id, integrationId)
|
|
62909
|
+
where: and(eq(gameTimebackIntegrations.id, integrationId), isActiveGameTimebackIntegrationStatus())
|
|
62663
62910
|
});
|
|
62664
62911
|
if (!integration) {
|
|
62665
62912
|
throw new NotFoundError(`Integration not found: ${integrationId}`);
|
|
@@ -62690,7 +62937,7 @@ class TimebackAssessmentsService {
|
|
|
62690
62937
|
}
|
|
62691
62938
|
});
|
|
62692
62939
|
} catch (error) {
|
|
62693
|
-
if (!(error instanceof
|
|
62940
|
+
if (!(error instanceof TimebackApiError3 && error.status === 409)) {
|
|
62694
62941
|
throw error;
|
|
62695
62942
|
}
|
|
62696
62943
|
}
|
|
@@ -62712,7 +62959,7 @@ class TimebackAssessmentsService {
|
|
|
62712
62959
|
}
|
|
62713
62960
|
});
|
|
62714
62961
|
} catch (error) {
|
|
62715
|
-
if (!(error instanceof
|
|
62962
|
+
if (!(error instanceof TimebackApiError3 && error.status === 409)) {
|
|
62716
62963
|
throw error;
|
|
62717
62964
|
}
|
|
62718
62965
|
}
|
|
@@ -62730,7 +62977,7 @@ class TimebackAssessmentsService {
|
|
|
62730
62977
|
}
|
|
62731
62978
|
});
|
|
62732
62979
|
} catch (error) {
|
|
62733
|
-
if (!(error instanceof
|
|
62980
|
+
if (!(error instanceof TimebackApiError3 && error.status === 409)) {
|
|
62734
62981
|
throw error;
|
|
62735
62982
|
}
|
|
62736
62983
|
}
|
|
@@ -62748,13 +62995,60 @@ class TimebackAssessmentsService {
|
|
|
62748
62995
|
}
|
|
62749
62996
|
var init_timeback_assessments_service = __esm(() => {
|
|
62750
62997
|
init_drizzle_orm();
|
|
62998
|
+
init_helpers_index();
|
|
62751
62999
|
init_tables_index();
|
|
62752
63000
|
init_spans();
|
|
62753
|
-
|
|
62754
|
-
|
|
63001
|
+
init_constants6();
|
|
63002
|
+
init_errors6();
|
|
62755
63003
|
init_utils6();
|
|
62756
63004
|
init_errors();
|
|
62757
63005
|
});
|
|
63006
|
+
function buildTimebackBaseConfigFromExistingConfig(config2) {
|
|
63007
|
+
return {
|
|
63008
|
+
organization: config2.organization,
|
|
63009
|
+
component: {
|
|
63010
|
+
...config2.component,
|
|
63011
|
+
title: ""
|
|
63012
|
+
},
|
|
63013
|
+
resource: {
|
|
63014
|
+
...config2.resource,
|
|
63015
|
+
title: ""
|
|
63016
|
+
},
|
|
63017
|
+
componentResource: {
|
|
63018
|
+
...config2.componentResource,
|
|
63019
|
+
title: ""
|
|
63020
|
+
}
|
|
63021
|
+
};
|
|
63022
|
+
}
|
|
63023
|
+
function getMasterableUnitsFromTimebackConfig(config2) {
|
|
63024
|
+
const playcademyMetadata = config2.resource.metadata?.playcademy;
|
|
63025
|
+
if (!isPlaycademyResourceMetadata(playcademyMetadata)) {
|
|
63026
|
+
return null;
|
|
63027
|
+
}
|
|
63028
|
+
return playcademyMetadata?.mastery?.masterableUnits ?? null;
|
|
63029
|
+
}
|
|
63030
|
+
function getTotalXpFromTimebackConfig(config2) {
|
|
63031
|
+
const courseMetadata = isCourseMetadata(config2.course.metadata) ? config2.course.metadata : undefined;
|
|
63032
|
+
if (typeof courseMetadata?.metrics?.totalXp === "number") {
|
|
63033
|
+
return courseMetadata.metrics.totalXp;
|
|
63034
|
+
}
|
|
63035
|
+
const resourceMetadata = config2.resource.metadata;
|
|
63036
|
+
if (isRecord2(resourceMetadata) && typeof resourceMetadata.xp === "number") {
|
|
63037
|
+
return resourceMetadata.xp;
|
|
63038
|
+
}
|
|
63039
|
+
return null;
|
|
63040
|
+
}
|
|
63041
|
+
function timebackConfigMatchesCreateIntegrationRequest(config2, request2) {
|
|
63042
|
+
const subject = config2.course.subjects[0];
|
|
63043
|
+
const grade = config2.course.grades[0];
|
|
63044
|
+
const requestedLevel = request2.level ?? deriveTimebackCourseLevelFromGrade(request2.grade);
|
|
63045
|
+
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;
|
|
63046
|
+
}
|
|
63047
|
+
var init_timeback_create_integration_util = __esm(() => {
|
|
63048
|
+
init_constants6();
|
|
63049
|
+
init_types2();
|
|
63050
|
+
init_timeback_util();
|
|
63051
|
+
});
|
|
62758
63052
|
async function promoteCompletedCourse({
|
|
62759
63053
|
db: db2,
|
|
62760
63054
|
client,
|
|
@@ -62763,7 +63057,7 @@ async function promoteCompletedCourse({
|
|
|
62763
63057
|
enrollments: prefetchedEnrollments
|
|
62764
63058
|
}) {
|
|
62765
63059
|
const subjectIntegrations = await db2.query.gameTimebackIntegrations.findMany({
|
|
62766
|
-
where: and(eq(gameTimebackIntegrations.gameId, currentIntegration.gameId), eq(gameTimebackIntegrations.subject, currentIntegration.subject))
|
|
63060
|
+
where: and(eq(gameTimebackIntegrations.gameId, currentIntegration.gameId), eq(gameTimebackIntegrations.subject, currentIntegration.subject), isActiveGameTimebackIntegrationStatus())
|
|
62767
63061
|
});
|
|
62768
63062
|
const nextIntegration = subjectIntegrations.filter((integration) => integration.grade > currentIntegration.grade).toSorted((left, right) => left.grade - right.grade)[0];
|
|
62769
63063
|
if (!nextIntegration) {
|
|
@@ -62818,19 +63112,68 @@ async function promoteCompletedCourse({
|
|
|
62818
63112
|
}
|
|
62819
63113
|
var init_timeback_promotion_util = __esm(() => {
|
|
62820
63114
|
init_drizzle_orm();
|
|
63115
|
+
init_helpers_index();
|
|
62821
63116
|
init_tables_index();
|
|
62822
63117
|
init_spans();
|
|
62823
63118
|
});
|
|
63119
|
+
function toGameTimebackIntegration(integration) {
|
|
63120
|
+
return {
|
|
63121
|
+
id: integration.id,
|
|
63122
|
+
gameId: integration.gameId,
|
|
63123
|
+
courseId: integration.courseId,
|
|
63124
|
+
grade: integration.grade,
|
|
63125
|
+
subject: integration.subject,
|
|
63126
|
+
totalXp: integration.totalXp ?? null,
|
|
63127
|
+
status: integration.status ?? ACTIVE_GAME_TIMEBACK_INTEGRATION_STATUS,
|
|
63128
|
+
deactivatedAt: integration.deactivatedAt ?? null,
|
|
63129
|
+
reactivatedAt: integration.reactivatedAt ?? null,
|
|
63130
|
+
createdAt: integration.createdAt,
|
|
63131
|
+
updatedAt: integration.updatedAt,
|
|
63132
|
+
lastVerifiedAt: integration.lastVerifiedAt ?? null
|
|
63133
|
+
};
|
|
63134
|
+
}
|
|
63135
|
+
function buildFallbackRemovedGameTimebackIntegration(integration) {
|
|
63136
|
+
if (!isTimebackSubject(integration.subject)) {
|
|
63137
|
+
throw new ValidationError(`Invalid subject "${integration.subject}"`);
|
|
63138
|
+
}
|
|
63139
|
+
if (!isTimebackGrade(integration.grade)) {
|
|
63140
|
+
throw new ValidationError(`Invalid grade "${integration.grade}"`);
|
|
63141
|
+
}
|
|
63142
|
+
return {
|
|
63143
|
+
integration: toGameTimebackIntegration(integration),
|
|
63144
|
+
title: `${integration.subject} ${formatGradeLabel(integration.grade)}`,
|
|
63145
|
+
courseCode: integration.courseId,
|
|
63146
|
+
subject: integration.subject,
|
|
63147
|
+
grade: integration.grade,
|
|
63148
|
+
totalXp: integration.totalXp ?? null,
|
|
63149
|
+
masterableUnits: null,
|
|
63150
|
+
removedAt: integration.deactivatedAt ?? null,
|
|
63151
|
+
metadata: null
|
|
63152
|
+
};
|
|
63153
|
+
}
|
|
63154
|
+
var init_timeback_removed_integration_util = __esm(() => {
|
|
63155
|
+
init_helpers_index();
|
|
63156
|
+
init_types2();
|
|
63157
|
+
init_timeback3();
|
|
63158
|
+
init_errors();
|
|
63159
|
+
});
|
|
63160
|
+
async function findGameTimebackIntegrationForUpdate(db2, condition) {
|
|
63161
|
+
const [integration] = await db2.select().from(gameTimebackIntegrations).where(condition).limit(1).for("update");
|
|
63162
|
+
return integration;
|
|
63163
|
+
}
|
|
62824
63164
|
var TimebackService;
|
|
62825
63165
|
var init_timeback_service = __esm(() => {
|
|
62826
63166
|
init_drizzle_orm();
|
|
62827
63167
|
init_src();
|
|
63168
|
+
init_helpers_index();
|
|
62828
63169
|
init_tables_index();
|
|
62829
63170
|
init_spans();
|
|
62830
63171
|
init_dist2();
|
|
62831
|
-
|
|
63172
|
+
init_types2();
|
|
62832
63173
|
init_errors();
|
|
63174
|
+
init_timeback_create_integration_util();
|
|
62833
63175
|
init_timeback_promotion_util();
|
|
63176
|
+
init_timeback_removed_integration_util();
|
|
62834
63177
|
init_timeback_util();
|
|
62835
63178
|
TimebackService = class TimebackService2 {
|
|
62836
63179
|
static HEARTBEAT_DEDUPE_TTL_MS = 300000;
|
|
@@ -63060,7 +63403,7 @@ var init_timeback_service = __esm(() => {
|
|
|
63060
63403
|
return [];
|
|
63061
63404
|
}
|
|
63062
63405
|
const integrations = await db2.query.gameTimebackIntegrations.findMany({
|
|
63063
|
-
where: inArray(gameTimebackIntegrations.courseId, courseIds)
|
|
63406
|
+
where: and(inArray(gameTimebackIntegrations.courseId, courseIds), isActiveGameTimebackIntegrationStatus())
|
|
63064
63407
|
});
|
|
63065
63408
|
return mapEnrollmentsToUserEnrollments(enrollments, integrations);
|
|
63066
63409
|
} catch (error) {
|
|
@@ -63085,6 +63428,7 @@ var init_timeback_service = __esm(() => {
|
|
|
63085
63428
|
const verboseData = [];
|
|
63086
63429
|
let integrationCreatedCount = 0;
|
|
63087
63430
|
let integrationUpdatedCount = 0;
|
|
63431
|
+
let integrationReactivatedCount = 0;
|
|
63088
63432
|
for (const courseConfig of courses) {
|
|
63089
63433
|
let applySuffix = function(text3) {
|
|
63090
63434
|
return suffix ? `${text3} ${suffix}` : text3;
|
|
@@ -63099,16 +63443,16 @@ var init_timeback_service = __esm(() => {
|
|
|
63099
63443
|
totalXp: derivedTotalXp,
|
|
63100
63444
|
masterableUnits: derivedMasterableUnits
|
|
63101
63445
|
} = courseConfig;
|
|
63102
|
-
if (!
|
|
63446
|
+
if (!isTimebackSubject(subjectInput)) {
|
|
63103
63447
|
throw new ValidationError(`Invalid subject "${subjectInput}"`);
|
|
63104
63448
|
}
|
|
63105
|
-
if (!
|
|
63449
|
+
if (!isTimebackGrade(grade)) {
|
|
63106
63450
|
throw new ValidationError(`Invalid grade "${grade}"`);
|
|
63107
63451
|
}
|
|
63108
63452
|
const subject = subjectInput;
|
|
63109
63453
|
const courseMetadata = isCourseMetadata(metadata2) ? metadata2 : undefined;
|
|
63110
63454
|
const totalXp = derivedTotalXp ?? courseMetadata?.metrics?.totalXp;
|
|
63111
|
-
const masterableUnits = derivedMasterableUnits ?? (
|
|
63455
|
+
const masterableUnits = derivedMasterableUnits ?? (isPlaycademyResourceMetadata(courseMetadata?.playcademy) ? courseMetadata?.playcademy?.mastery?.masterableUnits : undefined);
|
|
63112
63456
|
if (typeof totalXp !== "number") {
|
|
63113
63457
|
throw new ValidationError(`Course "${title}" is missing totalXp`);
|
|
63114
63458
|
}
|
|
@@ -63154,19 +63498,28 @@ var init_timeback_service = __esm(() => {
|
|
|
63154
63498
|
title: applySuffix(baseConfig.componentResource.title || "")
|
|
63155
63499
|
}
|
|
63156
63500
|
};
|
|
63157
|
-
const
|
|
63501
|
+
const matches = existing.filter((i2) => i2.grade === grade && i2.subject === subject);
|
|
63502
|
+
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
63503
|
if (existingIntegration) {
|
|
63159
|
-
await
|
|
63160
|
-
|
|
63504
|
+
const { updated, revived } = await this.updateSetupIntegration({
|
|
63505
|
+
client,
|
|
63506
|
+
existingIntegration,
|
|
63507
|
+
fullConfig,
|
|
63508
|
+
subject,
|
|
63509
|
+
totalXp
|
|
63510
|
+
});
|
|
63161
63511
|
if (updated) {
|
|
63162
|
-
integrations.push(
|
|
63512
|
+
integrations.push(toGameTimebackIntegration(updated));
|
|
63163
63513
|
integrationUpdatedCount++;
|
|
63514
|
+
if (revived) {
|
|
63515
|
+
integrationReactivatedCount++;
|
|
63516
|
+
}
|
|
63164
63517
|
}
|
|
63165
63518
|
} else {
|
|
63166
63519
|
const result = await client.setup(fullConfig, { verbose });
|
|
63167
63520
|
const [integration] = await db2.insert(gameTimebackIntegrations).values({ gameId, courseId: result.courseId, grade, subject, totalXp }).returning();
|
|
63168
63521
|
if (integration) {
|
|
63169
|
-
const dto =
|
|
63522
|
+
const dto = toGameTimebackIntegration(integration);
|
|
63170
63523
|
integrations.push(dto);
|
|
63171
63524
|
integrationCreatedCount++;
|
|
63172
63525
|
if (verbose && result.verboseData) {
|
|
@@ -63178,7 +63531,8 @@ var init_timeback_service = __esm(() => {
|
|
|
63178
63531
|
setAttributes({
|
|
63179
63532
|
"app.timeback.integration_count": integrations.length,
|
|
63180
63533
|
"app.timeback.integration_created_count": integrationCreatedCount,
|
|
63181
|
-
"app.timeback.integration_updated_count": integrationUpdatedCount
|
|
63534
|
+
"app.timeback.integration_updated_count": integrationUpdatedCount,
|
|
63535
|
+
"app.timeback.integration_reactivated_count": integrationReactivatedCount
|
|
63182
63536
|
});
|
|
63183
63537
|
return {
|
|
63184
63538
|
integrations,
|
|
@@ -63186,19 +63540,221 @@ var init_timeback_service = __esm(() => {
|
|
|
63186
63540
|
};
|
|
63187
63541
|
});
|
|
63188
63542
|
}
|
|
63543
|
+
async updateSetupIntegration(args2) {
|
|
63544
|
+
const { client, existingIntegration, fullConfig, subject, totalXp } = args2;
|
|
63545
|
+
const revived = existingIntegration.status === DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS;
|
|
63546
|
+
await client.update(existingIntegration.courseId, fullConfig);
|
|
63547
|
+
if (revived) {
|
|
63548
|
+
await client.reactivateCourse(existingIntegration.courseId);
|
|
63549
|
+
}
|
|
63550
|
+
const now2 = new Date;
|
|
63551
|
+
const [updated] = await this.deps.db.update(gameTimebackIntegrations).set({
|
|
63552
|
+
subject,
|
|
63553
|
+
totalXp,
|
|
63554
|
+
updatedAt: now2,
|
|
63555
|
+
...revived && {
|
|
63556
|
+
status: ACTIVE_GAME_TIMEBACK_INTEGRATION_STATUS,
|
|
63557
|
+
deactivatedAt: null,
|
|
63558
|
+
reactivatedAt: now2
|
|
63559
|
+
}
|
|
63560
|
+
}).where(eq(gameTimebackIntegrations.id, existingIntegration.id)).returning();
|
|
63561
|
+
return { updated, revived };
|
|
63562
|
+
}
|
|
63563
|
+
async createIntegration(gameId, user, request2) {
|
|
63564
|
+
return this.withClientTelemetry(async () => {
|
|
63565
|
+
const client = this.requireClient();
|
|
63566
|
+
const db2 = this.deps.db;
|
|
63567
|
+
await this.deps.validateDeveloperAccess(user, gameId);
|
|
63568
|
+
setAttributes({
|
|
63569
|
+
"app.timeback.grade": request2.grade,
|
|
63570
|
+
"app.timeback.subject": request2.subject
|
|
63571
|
+
});
|
|
63572
|
+
const activeConflict = await db2.query.gameTimebackIntegrations.findFirst({
|
|
63573
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.grade, request2.grade), eq(gameTimebackIntegrations.subject, request2.subject), isActiveGameTimebackIntegrationStatus())
|
|
63574
|
+
});
|
|
63575
|
+
if (activeConflict) {
|
|
63576
|
+
throw new ConflictError(`A TimeBack integration already exists for ${request2.subject} Grade ${request2.grade}`);
|
|
63577
|
+
}
|
|
63578
|
+
const removedCandidates = await db2.query.gameTimebackIntegrations.findMany({
|
|
63579
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.grade, request2.grade), eq(gameTimebackIntegrations.subject, request2.subject), eq(gameTimebackIntegrations.status, DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS))
|
|
63580
|
+
});
|
|
63581
|
+
for (const candidate of removedCandidates) {
|
|
63582
|
+
const config2 = await client.getConfig(candidate.courseId).catch((error) => {
|
|
63583
|
+
addEvent("timeback.removed_integration_config_fetch_failed", {
|
|
63584
|
+
"app.game.id": gameId,
|
|
63585
|
+
"app.timeback.course_id": candidate.courseId,
|
|
63586
|
+
"exception.type": errorType(error),
|
|
63587
|
+
"app.error.message": errorMessage(error)
|
|
63588
|
+
});
|
|
63589
|
+
return null;
|
|
63590
|
+
});
|
|
63591
|
+
if (config2 && timebackConfigMatchesCreateIntegrationRequest(config2, request2)) {
|
|
63592
|
+
const updated = await db2.transaction(async (tx) => {
|
|
63593
|
+
const database = tx;
|
|
63594
|
+
const lockedCandidate = await findGameTimebackIntegrationForUpdate(database, eq(gameTimebackIntegrations.id, candidate.id));
|
|
63595
|
+
if (lockedCandidate?.status !== DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS) {
|
|
63596
|
+
throw new ConflictError(`A TimeBack integration already exists for ${request2.subject} Grade ${request2.grade}`);
|
|
63597
|
+
}
|
|
63598
|
+
const now2 = new Date;
|
|
63599
|
+
const [integration2] = await database.update(gameTimebackIntegrations).set({
|
|
63600
|
+
status: ACTIVE_GAME_TIMEBACK_INTEGRATION_STATUS,
|
|
63601
|
+
totalXp: request2.totalXp,
|
|
63602
|
+
deactivatedAt: null,
|
|
63603
|
+
reactivatedAt: now2,
|
|
63604
|
+
updatedAt: now2
|
|
63605
|
+
}).where(and(eq(gameTimebackIntegrations.id, candidate.id), eq(gameTimebackIntegrations.status, DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS))).returning();
|
|
63606
|
+
if (!integration2) {
|
|
63607
|
+
throw new NotFoundError("Timeback integration", candidate.id);
|
|
63608
|
+
}
|
|
63609
|
+
await client.reactivateCourse(candidate.courseId);
|
|
63610
|
+
return integration2;
|
|
63611
|
+
});
|
|
63612
|
+
setAttribute("app.timeback.course_create_mode", "reactivated-removed");
|
|
63613
|
+
return toGameTimebackIntegration(updated);
|
|
63614
|
+
}
|
|
63615
|
+
}
|
|
63616
|
+
const reference = await db2.query.gameTimebackIntegrations.findFirst({
|
|
63617
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), isActiveGameTimebackIntegrationStatus()),
|
|
63618
|
+
orderBy: (table8, { asc: asc2 }) => [asc2(table8.createdAt)]
|
|
63619
|
+
}) ?? await db2.query.gameTimebackIntegrations.findFirst({
|
|
63620
|
+
where: eq(gameTimebackIntegrations.gameId, gameId),
|
|
63621
|
+
orderBy: (table8, { asc: asc2 }) => [asc2(table8.createdAt)]
|
|
63622
|
+
});
|
|
63623
|
+
if (!reference) {
|
|
63624
|
+
throw new ValidationError("Run `playcademy timeback setup` before adding courses from the dashboard.");
|
|
63625
|
+
}
|
|
63626
|
+
const referenceConfig = await client.getConfig(reference.courseId);
|
|
63627
|
+
const result = await this.setupIntegration(gameId, {
|
|
63628
|
+
gameId,
|
|
63629
|
+
courses: [
|
|
63630
|
+
{
|
|
63631
|
+
title: request2.title,
|
|
63632
|
+
courseCode: request2.courseCode,
|
|
63633
|
+
subject: request2.subject,
|
|
63634
|
+
grade: request2.grade,
|
|
63635
|
+
level: request2.level ?? deriveTimebackCourseLevelFromGrade(request2.grade),
|
|
63636
|
+
totalXp: request2.totalXp,
|
|
63637
|
+
masterableUnits: request2.masterableUnits
|
|
63638
|
+
}
|
|
63639
|
+
],
|
|
63640
|
+
baseConfig: buildTimebackBaseConfigFromExistingConfig(referenceConfig)
|
|
63641
|
+
}, user);
|
|
63642
|
+
const integration = result.integrations[0];
|
|
63643
|
+
if (!integration) {
|
|
63644
|
+
throw new InternalError("TimeBack course creation did not return an integration");
|
|
63645
|
+
}
|
|
63646
|
+
setAttribute("app.timeback.course_create_mode", "created-new");
|
|
63647
|
+
return integration;
|
|
63648
|
+
});
|
|
63649
|
+
}
|
|
63189
63650
|
async getIntegrations(gameId, user) {
|
|
63190
63651
|
await this.deps.validateGameManagementAccess(user, gameId);
|
|
63191
63652
|
const rows = await this.deps.db.query.gameTimebackIntegrations.findMany({
|
|
63192
|
-
where: eq(gameTimebackIntegrations.gameId, gameId)
|
|
63653
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), isActiveGameTimebackIntegrationStatus())
|
|
63654
|
+
});
|
|
63655
|
+
return rows.map((row) => toGameTimebackIntegration(row));
|
|
63656
|
+
}
|
|
63657
|
+
async getRemovedIntegrations(gameId, user) {
|
|
63658
|
+
return this.withClientTelemetry(async () => {
|
|
63659
|
+
const client = this.requireClient();
|
|
63660
|
+
await this.deps.validateGameManagementAccess(user, gameId);
|
|
63661
|
+
const rows = await this.deps.db.query.gameTimebackIntegrations.findMany({
|
|
63662
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.status, DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS)),
|
|
63663
|
+
orderBy: (table8, { asc: asc2 }) => [asc2(table8.subject), asc2(table8.grade)]
|
|
63664
|
+
});
|
|
63665
|
+
setAttribute("app.timeback.removed_integration_count", rows.length);
|
|
63666
|
+
return Promise.all(rows.map(async (row) => {
|
|
63667
|
+
try {
|
|
63668
|
+
const config2 = await client.getConfig(row.courseId);
|
|
63669
|
+
return this.toRemovedGameTimebackIntegration(row, config2);
|
|
63670
|
+
} catch (error) {
|
|
63671
|
+
addEvent("timeback.removed_integration_config_fetch_failed", {
|
|
63672
|
+
"app.game.id": gameId,
|
|
63673
|
+
"app.timeback.course_id": row.courseId,
|
|
63674
|
+
"exception.type": errorType(error),
|
|
63675
|
+
"app.error.message": errorMessage(error)
|
|
63676
|
+
});
|
|
63677
|
+
return buildFallbackRemovedGameTimebackIntegration(row);
|
|
63678
|
+
}
|
|
63679
|
+
}));
|
|
63680
|
+
});
|
|
63681
|
+
}
|
|
63682
|
+
async deactivateCourse(gameId, courseId, user) {
|
|
63683
|
+
return this.withClientTelemetry(async () => {
|
|
63684
|
+
const client = this.requireClient();
|
|
63685
|
+
const db2 = this.deps.db;
|
|
63686
|
+
await this.deps.validateDeveloperAccess(user, gameId);
|
|
63687
|
+
const updated = await db2.transaction(async (tx) => {
|
|
63688
|
+
const database = tx;
|
|
63689
|
+
const integration = await findGameTimebackIntegrationForUpdate(database, and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus()));
|
|
63690
|
+
if (!integration) {
|
|
63691
|
+
throw new NotFoundError("Timeback course", `${gameId}:${courseId}`);
|
|
63692
|
+
}
|
|
63693
|
+
const activeEnrollments = await client.oneroster.enrollments.listByCourse(courseId, {
|
|
63694
|
+
role: "student",
|
|
63695
|
+
includeUsers: false
|
|
63696
|
+
});
|
|
63697
|
+
setAttribute("app.timeback.active_enrollment_count", activeEnrollments.length);
|
|
63698
|
+
if (activeEnrollments.length > 0) {
|
|
63699
|
+
throw new ConflictError("Cannot remove course with active enrollments", {
|
|
63700
|
+
courseId,
|
|
63701
|
+
activeEnrollmentCount: activeEnrollments.length
|
|
63702
|
+
});
|
|
63703
|
+
}
|
|
63704
|
+
const now2 = new Date;
|
|
63705
|
+
const [updatedIntegration] = await database.update(gameTimebackIntegrations).set({
|
|
63706
|
+
status: DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS,
|
|
63707
|
+
deactivatedAt: now2,
|
|
63708
|
+
updatedAt: now2
|
|
63709
|
+
}).where(and(eq(gameTimebackIntegrations.id, integration.id), isActiveGameTimebackIntegrationStatus())).returning();
|
|
63710
|
+
if (!updatedIntegration) {
|
|
63711
|
+
throw new NotFoundError("Timeback course", `${gameId}:${courseId}`);
|
|
63712
|
+
}
|
|
63713
|
+
await client.deactivateCourse(courseId);
|
|
63714
|
+
return updatedIntegration;
|
|
63715
|
+
});
|
|
63716
|
+
return toGameTimebackIntegration(updated);
|
|
63717
|
+
});
|
|
63718
|
+
}
|
|
63719
|
+
async reactivateCourse(gameId, courseId, user) {
|
|
63720
|
+
return this.withClientTelemetry(async () => {
|
|
63721
|
+
const client = this.requireClient();
|
|
63722
|
+
const db2 = this.deps.db;
|
|
63723
|
+
await this.deps.validateDeveloperAccess(user, gameId);
|
|
63724
|
+
const updated = await db2.transaction(async (tx) => {
|
|
63725
|
+
const database = tx;
|
|
63726
|
+
const integration = await findGameTimebackIntegrationForUpdate(database, and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), eq(gameTimebackIntegrations.status, DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS)));
|
|
63727
|
+
if (!integration) {
|
|
63728
|
+
throw new NotFoundError("Removed Timeback course", `${gameId}:${courseId}`);
|
|
63729
|
+
}
|
|
63730
|
+
const activeConflict = await database.query.gameTimebackIntegrations.findFirst({
|
|
63731
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.grade, integration.grade), eq(gameTimebackIntegrations.subject, integration.subject), isActiveGameTimebackIntegrationStatus())
|
|
63732
|
+
});
|
|
63733
|
+
if (activeConflict) {
|
|
63734
|
+
throw new ConflictError(`An active TimeBack course already exists for ${integration.subject} Grade ${integration.grade}`);
|
|
63735
|
+
}
|
|
63736
|
+
const now2 = new Date;
|
|
63737
|
+
const [updatedIntegration] = await database.update(gameTimebackIntegrations).set({
|
|
63738
|
+
status: ACTIVE_GAME_TIMEBACK_INTEGRATION_STATUS,
|
|
63739
|
+
deactivatedAt: null,
|
|
63740
|
+
reactivatedAt: now2,
|
|
63741
|
+
updatedAt: now2
|
|
63742
|
+
}).where(and(eq(gameTimebackIntegrations.id, integration.id), eq(gameTimebackIntegrations.status, DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS))).returning();
|
|
63743
|
+
if (!updatedIntegration) {
|
|
63744
|
+
throw new NotFoundError("Timeback course", `${gameId}:${courseId}`);
|
|
63745
|
+
}
|
|
63746
|
+
await client.reactivateCourse(courseId);
|
|
63747
|
+
return updatedIntegration;
|
|
63748
|
+
});
|
|
63749
|
+
return toGameTimebackIntegration(updated);
|
|
63193
63750
|
});
|
|
63194
|
-
return rows.map((row) => this.toGameTimebackIntegration(row));
|
|
63195
63751
|
}
|
|
63196
63752
|
async getIntegrationConfig(gameId, courseId, user) {
|
|
63197
63753
|
return this.withClientTelemetry(async () => {
|
|
63198
63754
|
const client = this.requireClient();
|
|
63199
63755
|
await this.deps.validateGameManagementAccess(user, gameId);
|
|
63200
63756
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
63201
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
63757
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus())
|
|
63202
63758
|
});
|
|
63203
63759
|
if (!integration) {
|
|
63204
63760
|
throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
|
|
@@ -63215,15 +63771,15 @@ var init_timeback_service = __esm(() => {
|
|
|
63215
63771
|
"app.timeback.course_id": courseId
|
|
63216
63772
|
});
|
|
63217
63773
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
63218
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
63774
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId), isActiveGameTimebackIntegrationStatus())
|
|
63219
63775
|
});
|
|
63220
63776
|
if (!integration) {
|
|
63221
63777
|
throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
|
|
63222
63778
|
}
|
|
63223
63779
|
const timebackConfig = await client.getConfig(courseId);
|
|
63224
63780
|
const liveSubject = timebackConfig.course.subjects[0];
|
|
63225
|
-
const subject = patch.subject ?? (
|
|
63226
|
-
if (!
|
|
63781
|
+
const subject = patch.subject ?? (isTimebackSubject(liveSubject) ? liveSubject : integration.subject);
|
|
63782
|
+
if (!isTimebackSubject(subject)) {
|
|
63227
63783
|
throw new ValidationError(`Invalid subject "${subject}"`);
|
|
63228
63784
|
}
|
|
63229
63785
|
setAttributes({
|
|
@@ -63233,14 +63789,14 @@ var init_timeback_service = __esm(() => {
|
|
|
63233
63789
|
});
|
|
63234
63790
|
if (subject !== integration.subject) {
|
|
63235
63791
|
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))
|
|
63792
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.grade, integration.grade), eq(gameTimebackIntegrations.subject, subject), isActiveGameTimebackIntegrationStatus())
|
|
63237
63793
|
});
|
|
63238
63794
|
if (subjectConflict && subjectConflict.id !== integration.id) {
|
|
63239
63795
|
throw new ValidationError(`A TimeBack integration already exists for ${subject} Grade ${integration.grade}`);
|
|
63240
63796
|
}
|
|
63241
63797
|
}
|
|
63242
|
-
const totalXp = "totalXp" in patch ? patch.totalXp ?? null :
|
|
63243
|
-
const masterableUnits = "masterableUnits" in patch ? patch.masterableUnits ?? null :
|
|
63798
|
+
const totalXp = "totalXp" in patch ? patch.totalXp ?? null : getTotalXpFromTimebackConfig(timebackConfig) ?? integration.totalXp ?? null;
|
|
63799
|
+
const masterableUnits = "masterableUnits" in patch ? patch.masterableUnits ?? null : getMasterableUnitsFromTimebackConfig(timebackConfig);
|
|
63244
63800
|
setAttributes({
|
|
63245
63801
|
"app.timeback.total_xp": totalXp,
|
|
63246
63802
|
"app.timeback.masterable_units": masterableUnits
|
|
@@ -63260,7 +63816,7 @@ var init_timeback_service = __esm(() => {
|
|
|
63260
63816
|
if (!updated) {
|
|
63261
63817
|
throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
|
|
63262
63818
|
}
|
|
63263
|
-
return
|
|
63819
|
+
return toGameTimebackIntegration(updated);
|
|
63264
63820
|
});
|
|
63265
63821
|
}
|
|
63266
63822
|
async verifyIntegration(gameId, user) {
|
|
@@ -63270,7 +63826,7 @@ var init_timeback_service = __esm(() => {
|
|
|
63270
63826
|
await this.deps.validateDeveloperAccess(user, gameId);
|
|
63271
63827
|
TimebackService2.recordIntegrationOperation("verify_integration");
|
|
63272
63828
|
const integrations = await db2.query.gameTimebackIntegrations.findMany({
|
|
63273
|
-
where: eq(gameTimebackIntegrations.gameId, gameId)
|
|
63829
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), isActiveGameTimebackIntegrationStatus())
|
|
63274
63830
|
});
|
|
63275
63831
|
if (integrations.length === 0) {
|
|
63276
63832
|
throw new NotFoundError("Timeback integration", gameId);
|
|
@@ -63283,7 +63839,7 @@ var init_timeback_service = __esm(() => {
|
|
|
63283
63839
|
const errors3 = Object.entries(resources).filter(([_2, r]) => !r.found).map(([name3]) => `${name3} not found`);
|
|
63284
63840
|
const status = allFound ? "success" : "error";
|
|
63285
63841
|
return {
|
|
63286
|
-
integration:
|
|
63842
|
+
integration: toGameTimebackIntegration({
|
|
63287
63843
|
...integration,
|
|
63288
63844
|
lastVerifiedAt: now2
|
|
63289
63845
|
}),
|
|
@@ -63292,7 +63848,7 @@ var init_timeback_service = __esm(() => {
|
|
|
63292
63848
|
...errors3.length > 0 && { errors: errors3 }
|
|
63293
63849
|
};
|
|
63294
63850
|
}));
|
|
63295
|
-
await db2.update(gameTimebackIntegrations).set({ lastVerifiedAt: now2 }).where(
|
|
63851
|
+
await db2.update(gameTimebackIntegrations).set({ lastVerifiedAt: now2 }).where(inArray(gameTimebackIntegrations.id, integrations.map((integration) => integration.id)));
|
|
63296
63852
|
const overallStatus = results.every((r) => r.status === "success") ? "success" : "error";
|
|
63297
63853
|
const missingResourceCount = results.reduce((count, result) => count + (result.errors?.length ?? 0), 0);
|
|
63298
63854
|
setAttributes({
|
|
@@ -63308,7 +63864,7 @@ var init_timeback_service = __esm(() => {
|
|
|
63308
63864
|
const client = this.requireClient();
|
|
63309
63865
|
await this.deps.validateDeveloperAccess(user, gameId);
|
|
63310
63866
|
const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
63311
|
-
where: eq(gameTimebackIntegrations.gameId, gameId)
|
|
63867
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), isActiveGameTimebackIntegrationStatus())
|
|
63312
63868
|
});
|
|
63313
63869
|
if (!integration) {
|
|
63314
63870
|
throw new NotFoundError("Timeback integration", gameId);
|
|
@@ -63335,24 +63891,6 @@ var init_timeback_service = __esm(() => {
|
|
|
63335
63891
|
await db2.delete(gameTimebackIntegrations).where(eq(gameTimebackIntegrations.gameId, gameId));
|
|
63336
63892
|
});
|
|
63337
63893
|
}
|
|
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
63894
|
static patchCourseMetadata(metadata2, totalXp, options) {
|
|
63357
63895
|
const nextMetadata = isRecord2(metadata2) ? { ...metadata2 } : {};
|
|
63358
63896
|
const currentMetrics = isRecord2(nextMetadata.metrics) ? nextMetadata.metrics : {};
|
|
@@ -63475,31 +64013,29 @@ var init_timeback_service = __esm(() => {
|
|
|
63475
64013
|
}
|
|
63476
64014
|
};
|
|
63477
64015
|
}
|
|
63478
|
-
|
|
64016
|
+
toRemovedGameTimebackIntegration(integration, config2) {
|
|
64017
|
+
const grade = config2.course.grades[0] ?? integration.grade;
|
|
64018
|
+
if (!isTimebackGrade(grade)) {
|
|
64019
|
+
throw new ValidationError(`Invalid grade "${grade}"`);
|
|
64020
|
+
}
|
|
63479
64021
|
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
|
|
64022
|
+
...this.toGameTimebackIntegrationConfig(integration, config2),
|
|
64023
|
+
grade,
|
|
64024
|
+
removedAt: integration.deactivatedAt ?? null
|
|
63489
64025
|
};
|
|
63490
64026
|
}
|
|
63491
64027
|
toGameTimebackIntegrationConfig(integration, config2) {
|
|
63492
64028
|
const subject = config2.course.subjects[0] ?? integration.subject;
|
|
63493
|
-
if (!
|
|
64029
|
+
if (!isTimebackSubject(subject)) {
|
|
63494
64030
|
throw new ValidationError(`Invalid subject "${subject}"`);
|
|
63495
64031
|
}
|
|
63496
64032
|
return {
|
|
63497
|
-
integration:
|
|
64033
|
+
integration: toGameTimebackIntegration(integration),
|
|
63498
64034
|
title: config2.course.title,
|
|
63499
64035
|
courseCode: config2.course.courseCode,
|
|
63500
64036
|
subject,
|
|
63501
|
-
totalXp:
|
|
63502
|
-
masterableUnits:
|
|
64037
|
+
totalXp: getTotalXpFromTimebackConfig(config2) ?? integration.totalXp ?? null,
|
|
64038
|
+
masterableUnits: getMasterableUnitsFromTimebackConfig(config2),
|
|
63503
64039
|
metadata: isCourseMetadata(config2.course.metadata) ? config2.course.metadata : null
|
|
63504
64040
|
};
|
|
63505
64041
|
}
|
|
@@ -63531,7 +64067,7 @@ var init_timeback_service = __esm(() => {
|
|
|
63531
64067
|
});
|
|
63532
64068
|
await this.deps.validateDeveloperAccess(user, gameId);
|
|
63533
64069
|
const integration = await db2.query.gameTimebackIntegrations.findFirst({
|
|
63534
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.grade, activityData.grade), eq(gameTimebackIntegrations.subject, activityData.subject))
|
|
64070
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.grade, activityData.grade), eq(gameTimebackIntegrations.subject, activityData.subject), isActiveGameTimebackIntegrationStatus())
|
|
63535
64071
|
});
|
|
63536
64072
|
if (!integration) {
|
|
63537
64073
|
throw new NotFoundError(`Timeback integration for game (grade ${activityData.grade}, subject ${activityData.subject})`);
|
|
@@ -63592,9 +64128,7 @@ var init_timeback_service = __esm(() => {
|
|
|
63592
64128
|
"app.timeback.xp_awarded": result.xpAwarded,
|
|
63593
64129
|
"app.timeback.mastered_units_requested": masteredUnits,
|
|
63594
64130
|
"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
|
|
64131
|
+
"app.timeback.mastered_units_applied": result.masteredUnitsApplied
|
|
63598
64132
|
});
|
|
63599
64133
|
return {
|
|
63600
64134
|
status: "ok",
|
|
@@ -63602,8 +64136,6 @@ var init_timeback_service = __esm(() => {
|
|
|
63602
64136
|
xpAwarded: result.xpAwarded,
|
|
63603
64137
|
masteredUnits: result.masteredUnitsApplied,
|
|
63604
64138
|
pctCompleteApp: result.pctCompleteApp,
|
|
63605
|
-
scoreStatus: result.scoreStatus,
|
|
63606
|
-
inProgress: result.inProgress,
|
|
63607
64139
|
...result.warnings ? { warnings: result.warnings } : {}
|
|
63608
64140
|
};
|
|
63609
64141
|
}
|
|
@@ -63616,7 +64148,7 @@ var init_timeback_service = __esm(() => {
|
|
|
63616
64148
|
const client = this.requireClient();
|
|
63617
64149
|
const db2 = this.deps.db;
|
|
63618
64150
|
const integrations = await db2.query.gameTimebackIntegrations.findMany({
|
|
63619
|
-
where: subject ? and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.subject, subject)) : eq(gameTimebackIntegrations.gameId, gameId)
|
|
64151
|
+
where: subject ? and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.subject, subject), isActiveGameTimebackIntegrationStatus()) : and(eq(gameTimebackIntegrations.gameId, gameId), isActiveGameTimebackIntegrationStatus())
|
|
63620
64152
|
});
|
|
63621
64153
|
if (integrations.length === 0) {
|
|
63622
64154
|
throw new NotFoundError(subject ? `Timeback integrations for game (subject ${subject})` : "Timeback integrations for game");
|
|
@@ -63798,7 +64330,7 @@ var init_timeback_service = __esm(() => {
|
|
|
63798
64330
|
}
|
|
63799
64331
|
const pendingHeartbeat = (async () => {
|
|
63800
64332
|
const integration = await db2.query.gameTimebackIntegrations.findFirst({
|
|
63801
|
-
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.grade, activityData.grade), eq(gameTimebackIntegrations.subject, activityData.subject))
|
|
64333
|
+
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.grade, activityData.grade), eq(gameTimebackIntegrations.subject, activityData.subject), isActiveGameTimebackIntegrationStatus())
|
|
63802
64334
|
});
|
|
63803
64335
|
if (!integration) {
|
|
63804
64336
|
throw new NotFoundError(`Timeback integration for game (grade ${activityData.grade}, subject ${activityData.subject})`);
|
|
@@ -63846,7 +64378,10 @@ var init_timeback_service = __esm(() => {
|
|
|
63846
64378
|
let courseIds = [];
|
|
63847
64379
|
if (options?.gameId) {
|
|
63848
64380
|
await this.deps.validateDeveloperAccess(user, options.gameId);
|
|
63849
|
-
const conditions2 = [
|
|
64381
|
+
const conditions2 = [
|
|
64382
|
+
eq(gameTimebackIntegrations.gameId, options.gameId),
|
|
64383
|
+
isActiveGameTimebackIntegrationStatus()
|
|
64384
|
+
];
|
|
63850
64385
|
if (options.grade !== undefined && options.subject) {
|
|
63851
64386
|
conditions2.push(eq(gameTimebackIntegrations.grade, options.grade));
|
|
63852
64387
|
conditions2.push(eq(gameTimebackIntegrations.subject, options.subject));
|
|
@@ -63873,7 +64408,10 @@ var init_timeback_service = __esm(() => {
|
|
|
63873
64408
|
const client = this.requireClient();
|
|
63874
64409
|
const db2 = this.deps.db;
|
|
63875
64410
|
await this.deps.validateDeveloperAccess(user, options.gameId);
|
|
63876
|
-
const conditions2 = [
|
|
64411
|
+
const conditions2 = [
|
|
64412
|
+
eq(gameTimebackIntegrations.gameId, options.gameId),
|
|
64413
|
+
isActiveGameTimebackIntegrationStatus()
|
|
64414
|
+
];
|
|
63877
64415
|
if (options.grade !== undefined && options.subject) {
|
|
63878
64416
|
conditions2.push(eq(gameTimebackIntegrations.grade, options.grade));
|
|
63879
64417
|
conditions2.push(eq(gameTimebackIntegrations.subject, options.subject));
|
|
@@ -63900,7 +64438,7 @@ var init_timeback_service = __esm(() => {
|
|
|
63900
64438
|
const db2 = this.deps.db;
|
|
63901
64439
|
await this.deps.validateDeveloperAccess(user, options.gameId);
|
|
63902
64440
|
const integration = await db2.query.gameTimebackIntegrations.findFirst({
|
|
63903
|
-
where: and(eq(gameTimebackIntegrations.gameId, options.gameId), eq(gameTimebackIntegrations.subject, options.subject))
|
|
64441
|
+
where: and(eq(gameTimebackIntegrations.gameId, options.gameId), eq(gameTimebackIntegrations.subject, options.subject), isActiveGameTimebackIntegrationStatus())
|
|
63904
64442
|
});
|
|
63905
64443
|
if (!integration) {
|
|
63906
64444
|
throw new ValidationError(`Subject "${options.subject}" is not configured for game ${options.gameId}`);
|
|
@@ -64767,7 +65305,7 @@ class UserService {
|
|
|
64767
65305
|
return [];
|
|
64768
65306
|
}
|
|
64769
65307
|
const integrations = await this.deps.db.query.gameTimebackIntegrations.findMany({
|
|
64770
|
-
where: inArray(gameTimebackIntegrations.courseId, courseIds)
|
|
65308
|
+
where: and(inArray(gameTimebackIntegrations.courseId, courseIds), isActiveGameTimebackIntegrationStatus())
|
|
64771
65309
|
});
|
|
64772
65310
|
return mapEnrollmentsToUserEnrollments(enrollments, integrations);
|
|
64773
65311
|
} catch (error) {
|
|
@@ -64789,6 +65327,7 @@ class UserService {
|
|
|
64789
65327
|
var init_user_service = __esm(() => {
|
|
64790
65328
|
init_drizzle_orm();
|
|
64791
65329
|
init_src();
|
|
65330
|
+
init_helpers_index();
|
|
64792
65331
|
init_tables_index();
|
|
64793
65332
|
init_spans();
|
|
64794
65333
|
init_errors();
|
|
@@ -81189,7 +81728,7 @@ function extractTablesRelationalConfig2(schema5, configHelpers) {
|
|
|
81189
81728
|
return { tables: tablesConfig, tableNamesMap };
|
|
81190
81729
|
}
|
|
81191
81730
|
function relations3(table62, relations22) {
|
|
81192
|
-
return new Relations2(table62, (
|
|
81731
|
+
return new Relations2(table62, (helpers3) => Object.fromEntries(Object.entries(relations22(helpers3)).map(([key, value]) => [
|
|
81193
81732
|
key,
|
|
81194
81733
|
value.withFieldName(key)
|
|
81195
81734
|
])));
|
|
@@ -82452,7 +82991,7 @@ var errorMap2;
|
|
|
82452
82991
|
var en_default2;
|
|
82453
82992
|
var init_en2;
|
|
82454
82993
|
var overrideErrorMap2;
|
|
82455
|
-
var
|
|
82994
|
+
var init_errors8;
|
|
82456
82995
|
var makeIssue2;
|
|
82457
82996
|
var ParseStatus2;
|
|
82458
82997
|
var INVALID2;
|
|
@@ -88689,7 +89228,7 @@ See: https://github.com/isaacs/node-glob/issues/167`);
|
|
|
88689
89228
|
en_default2 = errorMap2;
|
|
88690
89229
|
}
|
|
88691
89230
|
});
|
|
88692
|
-
|
|
89231
|
+
init_errors8 = __esm7({
|
|
88693
89232
|
"../node_modules/.pnpm/zod@3.25.42/node_modules/zod/dist/esm/v3/errors.js"() {
|
|
88694
89233
|
init_en2();
|
|
88695
89234
|
overrideErrorMap2 = en_default2;
|
|
@@ -88697,7 +89236,7 @@ See: https://github.com/isaacs/node-glob/issues/167`);
|
|
|
88697
89236
|
});
|
|
88698
89237
|
init_parseUtil2 = __esm7({
|
|
88699
89238
|
"../node_modules/.pnpm/zod@3.25.42/node_modules/zod/dist/esm/v3/helpers/parseUtil.js"() {
|
|
88700
|
-
|
|
89239
|
+
init_errors8();
|
|
88701
89240
|
init_en2();
|
|
88702
89241
|
makeIssue2 = (params) => {
|
|
88703
89242
|
const { data, path: path22, errorMaps, issueData } = params;
|
|
@@ -88803,7 +89342,7 @@ See: https://github.com/isaacs/node-glob/issues/167`);
|
|
|
88803
89342
|
init_types5 = __esm7({
|
|
88804
89343
|
"../node_modules/.pnpm/zod@3.25.42/node_modules/zod/dist/esm/v3/types.js"() {
|
|
88805
89344
|
init_ZodError2();
|
|
88806
|
-
|
|
89345
|
+
init_errors8();
|
|
88807
89346
|
init_errorUtil2();
|
|
88808
89347
|
init_parseUtil2();
|
|
88809
89348
|
init_util2();
|
|
@@ -91961,7 +92500,7 @@ See: https://github.com/isaacs/node-glob/issues/167`);
|
|
|
91961
92500
|
});
|
|
91962
92501
|
init_external2 = __esm7({
|
|
91963
92502
|
"../node_modules/.pnpm/zod@3.25.42/node_modules/zod/dist/esm/v3/external.js"() {
|
|
91964
|
-
|
|
92503
|
+
init_errors8();
|
|
91965
92504
|
init_parseUtil2();
|
|
91966
92505
|
init_typeAliases2();
|
|
91967
92506
|
init_util2();
|
|
@@ -122615,7 +123154,7 @@ async function seedTimebackIntegrations(db2, gameId, courses) {
|
|
|
122615
123154
|
const courseId = course.courseId || `mock-${course.subject.toLowerCase()}-g${course.grade}`;
|
|
122616
123155
|
try {
|
|
122617
123156
|
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))
|
|
123157
|
+
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
123158
|
});
|
|
122620
123159
|
if (!existing) {
|
|
122621
123160
|
await db2.insert(gameTimebackIntegrations).values({
|
|
@@ -122635,6 +123174,7 @@ async function seedTimebackIntegrations(db2, gameId, courses) {
|
|
|
122635
123174
|
return seededCount;
|
|
122636
123175
|
}
|
|
122637
123176
|
var init_timeback5 = __esm(() => {
|
|
123177
|
+
init_helpers_index();
|
|
122638
123178
|
init_tables_index();
|
|
122639
123179
|
init_config();
|
|
122640
123180
|
});
|
|
@@ -123840,6 +124380,7 @@ var init_game_member_controller = __esm(() => {
|
|
|
123840
124380
|
var list2;
|
|
123841
124381
|
var listAccessible;
|
|
123842
124382
|
var getSubjects;
|
|
124383
|
+
var getTimebackSummaries;
|
|
123843
124384
|
var getById;
|
|
123844
124385
|
var getBySlug;
|
|
123845
124386
|
var getManifest;
|
|
@@ -123855,6 +124396,7 @@ var init_game_controller = __esm(() => {
|
|
|
123855
124396
|
list2 = requireNonAnonymous(async (ctx) => ctx.services.game.list(ctx.user));
|
|
123856
124397
|
listAccessible = requireNonAnonymous(async (ctx) => ctx.services.game.listAccessible(ctx.user));
|
|
123857
124398
|
getSubjects = requireNonAnonymous(async (ctx) => ctx.services.game.getSubjects());
|
|
124399
|
+
getTimebackSummaries = requireGameManagementAccess(async (ctx) => ctx.services.game.getTimebackSummaries(ctx.user));
|
|
123858
124400
|
getById = requireNonAnonymous(async (ctx) => {
|
|
123859
124401
|
const gameId = requireGameId(ctx.params.gameId);
|
|
123860
124402
|
return ctx.services.game.getById(gameId, ctx.user);
|
|
@@ -123902,6 +124444,7 @@ var init_game_controller = __esm(() => {
|
|
|
123902
124444
|
list: list2,
|
|
123903
124445
|
listAccessible,
|
|
123904
124446
|
getSubjects,
|
|
124447
|
+
getTimebackSummaries,
|
|
123905
124448
|
getById,
|
|
123906
124449
|
getManifest,
|
|
123907
124450
|
getBySlug,
|
|
@@ -124305,7 +124848,11 @@ var getUserEnrollments;
|
|
|
124305
124848
|
var getUserById;
|
|
124306
124849
|
var setupIntegration;
|
|
124307
124850
|
var getIntegrations;
|
|
124851
|
+
var getRemovedIntegrations;
|
|
124852
|
+
var createIntegration;
|
|
124308
124853
|
var updateIntegration;
|
|
124854
|
+
var deactivateCourse;
|
|
124855
|
+
var reactivateCourse;
|
|
124309
124856
|
var getIntegrationConfig;
|
|
124310
124857
|
var verifyIntegration;
|
|
124311
124858
|
var getConfig;
|
|
@@ -124403,6 +124950,27 @@ var init_timeback_controller = __esm(() => {
|
|
|
124403
124950
|
}
|
|
124404
124951
|
return ctx.services.timeback.getIntegrations(gameId, ctx.user);
|
|
124405
124952
|
});
|
|
124953
|
+
getRemovedIntegrations = requireGameManagementAccess(async (ctx) => {
|
|
124954
|
+
const gameId = ctx.params.gameId;
|
|
124955
|
+
if (!gameId) {
|
|
124956
|
+
throw ApiError.badRequest("Missing gameId");
|
|
124957
|
+
}
|
|
124958
|
+
if (!isValidUUID(gameId)) {
|
|
124959
|
+
throw ApiError.unprocessableEntity("Invalid gameId format");
|
|
124960
|
+
}
|
|
124961
|
+
return ctx.services.timeback.getRemovedIntegrations(gameId, ctx.user);
|
|
124962
|
+
});
|
|
124963
|
+
createIntegration = requireDeveloper(async (ctx) => {
|
|
124964
|
+
const gameId = ctx.params.gameId;
|
|
124965
|
+
if (!gameId) {
|
|
124966
|
+
throw ApiError.badRequest("Missing gameId");
|
|
124967
|
+
}
|
|
124968
|
+
if (!isValidUUID(gameId)) {
|
|
124969
|
+
throw ApiError.unprocessableEntity("Invalid gameId format");
|
|
124970
|
+
}
|
|
124971
|
+
const body2 = await parseRequestBody(ctx.request, CreateGameTimebackIntegrationRequestSchema);
|
|
124972
|
+
return ctx.services.timeback.createIntegration(gameId, ctx.user, body2);
|
|
124973
|
+
});
|
|
124406
124974
|
updateIntegration = requireDeveloper(async (ctx) => {
|
|
124407
124975
|
const { gameId, courseId } = ctx.params;
|
|
124408
124976
|
if (!gameId || !courseId) {
|
|
@@ -124414,6 +124982,26 @@ var init_timeback_controller = __esm(() => {
|
|
|
124414
124982
|
const body2 = await parseRequestBody(ctx.request, UpdateGameTimebackIntegrationRequestSchema);
|
|
124415
124983
|
return ctx.services.timeback.updateIntegration(gameId, courseId, ctx.user, body2);
|
|
124416
124984
|
});
|
|
124985
|
+
deactivateCourse = requireDeveloper(async (ctx) => {
|
|
124986
|
+
const { gameId, courseId } = ctx.params;
|
|
124987
|
+
if (!gameId || !courseId) {
|
|
124988
|
+
throw ApiError.badRequest("Missing gameId or courseId parameter");
|
|
124989
|
+
}
|
|
124990
|
+
if (!isValidUUID(gameId)) {
|
|
124991
|
+
throw ApiError.unprocessableEntity("Invalid gameId format");
|
|
124992
|
+
}
|
|
124993
|
+
return ctx.services.timeback.deactivateCourse(gameId, courseId, ctx.user);
|
|
124994
|
+
});
|
|
124995
|
+
reactivateCourse = requireDeveloper(async (ctx) => {
|
|
124996
|
+
const { gameId, courseId } = ctx.params;
|
|
124997
|
+
if (!gameId || !courseId) {
|
|
124998
|
+
throw ApiError.badRequest("Missing gameId or courseId parameter");
|
|
124999
|
+
}
|
|
125000
|
+
if (!isValidUUID(gameId)) {
|
|
125001
|
+
throw ApiError.unprocessableEntity("Invalid gameId format");
|
|
125002
|
+
}
|
|
125003
|
+
return ctx.services.timeback.reactivateCourse(gameId, courseId, ctx.user);
|
|
125004
|
+
});
|
|
124417
125005
|
getIntegrationConfig = requireGameManagementAccess(async (ctx) => {
|
|
124418
125006
|
const { gameId, courseId } = ctx.params;
|
|
124419
125007
|
if (!gameId || !courseId) {
|
|
@@ -124563,11 +125151,11 @@ var init_timeback_controller = __esm(() => {
|
|
|
124563
125151
|
let subject;
|
|
124564
125152
|
if (gradeParam !== null && subjectParam !== null) {
|
|
124565
125153
|
const parsedGrade = parseInt(gradeParam, 10);
|
|
124566
|
-
if (!
|
|
125154
|
+
if (!isTimebackGrade2(parsedGrade)) {
|
|
124567
125155
|
throw ApiError.badRequest(`Invalid grade: ${gradeParam}. Valid grades: ${TIMEBACK_GRADES.join(", ")}`);
|
|
124568
125156
|
}
|
|
124569
|
-
if (!
|
|
124570
|
-
throw ApiError.badRequest(`Invalid subject: ${subjectParam}. Valid subjects: ${
|
|
125157
|
+
if (!isTimebackSubject2(subjectParam)) {
|
|
125158
|
+
throw ApiError.badRequest(`Invalid subject: ${subjectParam}. Valid subjects: ${TIMEBACK_SUBJECTS2.join(", ")}`);
|
|
124571
125159
|
}
|
|
124572
125160
|
grade = parsedGrade;
|
|
124573
125161
|
subject = subjectParam;
|
|
@@ -124603,11 +125191,11 @@ var init_timeback_controller = __esm(() => {
|
|
|
124603
125191
|
let subject;
|
|
124604
125192
|
if (gradeParam !== null && subjectParam !== null) {
|
|
124605
125193
|
const parsedGrade = parseInt(gradeParam, 10);
|
|
124606
|
-
if (!
|
|
125194
|
+
if (!isTimebackGrade2(parsedGrade)) {
|
|
124607
125195
|
throw ApiError.badRequest(`Invalid grade: ${gradeParam}. Valid grades: ${TIMEBACK_GRADES.join(", ")}`);
|
|
124608
125196
|
}
|
|
124609
|
-
if (!
|
|
124610
|
-
throw ApiError.badRequest(`Invalid subject: ${subjectParam}. Valid subjects: ${
|
|
125197
|
+
if (!isTimebackSubject2(subjectParam)) {
|
|
125198
|
+
throw ApiError.badRequest(`Invalid subject: ${subjectParam}. Valid subjects: ${TIMEBACK_SUBJECTS2.join(", ")}`);
|
|
124611
125199
|
}
|
|
124612
125200
|
grade = parsedGrade;
|
|
124613
125201
|
subject = subjectParam;
|
|
@@ -124637,8 +125225,8 @@ var init_timeback_controller = __esm(() => {
|
|
|
124637
125225
|
if (!subjectParam) {
|
|
124638
125226
|
throw ApiError.badRequest("Missing required subject query parameter");
|
|
124639
125227
|
}
|
|
124640
|
-
if (!
|
|
124641
|
-
throw ApiError.badRequest(`Invalid subject: ${subjectParam}. Valid subjects: ${
|
|
125228
|
+
if (!isTimebackSubject2(subjectParam)) {
|
|
125229
|
+
throw ApiError.badRequest(`Invalid subject: ${subjectParam}. Valid subjects: ${TIMEBACK_SUBJECTS2.join(", ")}`);
|
|
124642
125230
|
}
|
|
124643
125231
|
return ctx.services.timeback.getStudentHighestGradeMastered(timebackId, ctx.user, {
|
|
124644
125232
|
gameId,
|
|
@@ -124949,7 +125537,11 @@ var init_timeback_controller = __esm(() => {
|
|
|
124949
125537
|
getUserById,
|
|
124950
125538
|
setupIntegration,
|
|
124951
125539
|
getIntegrations,
|
|
125540
|
+
getRemovedIntegrations,
|
|
125541
|
+
createIntegration,
|
|
124952
125542
|
updateIntegration,
|
|
125543
|
+
deactivateCourse,
|
|
125544
|
+
reactivateCourse,
|
|
124953
125545
|
getIntegrationConfig,
|
|
124954
125546
|
verifyIntegration,
|
|
124955
125547
|
getConfig,
|
|
@@ -125909,12 +126501,12 @@ var init_timeback7 = __esm(() => {
|
|
|
125909
126501
|
}
|
|
125910
126502
|
if (gradeParam !== null && subjectParam !== null) {
|
|
125911
126503
|
const grade = parseInt(gradeParam, 10);
|
|
125912
|
-
if (!Number.isFinite(grade) || !
|
|
126504
|
+
if (!Number.isFinite(grade) || !isTimebackGrade2(grade)) {
|
|
125913
126505
|
const error2 = ApiError.badRequest(`Invalid grade: ${gradeParam}. Valid grades: ${TIMEBACK_GRADES.join(", ")}`);
|
|
125914
126506
|
return c2.json(createErrorResponse(error2), error2.status);
|
|
125915
126507
|
}
|
|
125916
|
-
if (!
|
|
125917
|
-
const error2 = ApiError.badRequest(`Invalid subject: ${subjectParam}. Valid subjects: ${
|
|
126508
|
+
if (!isTimebackSubject2(subjectParam)) {
|
|
126509
|
+
const error2 = ApiError.badRequest(`Invalid subject: ${subjectParam}. Valid subjects: ${TIMEBACK_SUBJECTS2.join(", ")}`);
|
|
125918
126510
|
return c2.json(createErrorResponse(error2), error2.status);
|
|
125919
126511
|
}
|
|
125920
126512
|
enrollments = enrollments.filter((e) => e.grade === grade && e.subject === subjectParam);
|
|
@@ -125967,12 +126559,12 @@ var init_timeback7 = __esm(() => {
|
|
|
125967
126559
|
}
|
|
125968
126560
|
if (gradeParam !== null && subjectParam !== null) {
|
|
125969
126561
|
const grade = parseInt(gradeParam, 10);
|
|
125970
|
-
if (!Number.isFinite(grade) || !
|
|
126562
|
+
if (!Number.isFinite(grade) || !isTimebackGrade2(grade)) {
|
|
125971
126563
|
const error2 = ApiError.badRequest(`Invalid grade: ${gradeParam}. Valid grades: ${TIMEBACK_GRADES.join(", ")}`);
|
|
125972
126564
|
return c2.json(createErrorResponse(error2), error2.status);
|
|
125973
126565
|
}
|
|
125974
|
-
if (!
|
|
125975
|
-
const error2 = ApiError.badRequest(`Invalid subject: ${subjectParam}. Valid subjects: ${
|
|
126566
|
+
if (!isTimebackSubject2(subjectParam)) {
|
|
126567
|
+
const error2 = ApiError.badRequest(`Invalid subject: ${subjectParam}. Valid subjects: ${TIMEBACK_SUBJECTS2.join(", ")}`);
|
|
125976
126568
|
return c2.json(createErrorResponse(error2), error2.status);
|
|
125977
126569
|
}
|
|
125978
126570
|
enrollments = enrollments.filter((e) => e.grade === grade && e.subject === subjectParam);
|
|
@@ -126021,8 +126613,8 @@ var init_timeback7 = __esm(() => {
|
|
|
126021
126613
|
const error2 = ApiError.badRequest("Missing required gameId query parameter");
|
|
126022
126614
|
return c2.json(createErrorResponse(error2), error2.status);
|
|
126023
126615
|
}
|
|
126024
|
-
if (!
|
|
126025
|
-
const error2 = ApiError.badRequest(`Invalid subject: ${subject}. Valid subjects: ${
|
|
126616
|
+
if (!isTimebackSubject2(subject)) {
|
|
126617
|
+
const error2 = ApiError.badRequest(`Invalid subject: ${subject}. Valid subjects: ${TIMEBACK_SUBJECTS2.join(", ")}`);
|
|
126026
126618
|
return c2.json(createErrorResponse(error2), error2.status);
|
|
126027
126619
|
}
|
|
126028
126620
|
const db2 = c2.get("db");
|
|
@@ -126251,12 +126843,12 @@ import path3 from "node:path";
|
|
|
126251
126843
|
import { loadPlaycademyConfig as loadPlaycademyConfig2 } from "playcademy/utils";
|
|
126252
126844
|
|
|
126253
126845
|
// ../utils/src/uuid.ts
|
|
126254
|
-
var
|
|
126846
|
+
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
126847
|
function isValidUUID2(value) {
|
|
126256
126848
|
if (!value || typeof value !== "string") {
|
|
126257
126849
|
return false;
|
|
126258
126850
|
}
|
|
126259
|
-
return
|
|
126851
|
+
return UUID_REGEX4.test(value);
|
|
126260
126852
|
}
|
|
126261
126853
|
// ../utils/src/ansi.ts
|
|
126262
126854
|
var colors3 = {
|
|
@@ -126703,7 +127295,8 @@ var DEMO_DISPLAY_NAME_PLACEHOLDER2 = "Demo Player";
|
|
|
126703
127295
|
var init_auth3 = __esm8(() => {
|
|
126704
127296
|
AUTH_PROVIDER_IDS2 = {
|
|
126705
127297
|
TIMEBACK: "timeback",
|
|
126706
|
-
TIMEBACK_LTI: "timeback-lti"
|
|
127298
|
+
TIMEBACK_LTI: "timeback-lti",
|
|
127299
|
+
PLAYCADEMY: "playcademy"
|
|
126707
127300
|
};
|
|
126708
127301
|
});
|
|
126709
127302
|
var init_typescript2 = () => {};
|