@playcademy/sandbox 0.12.2-beta.3 → 0.12.2-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/cli.js +404 -92
- package/dist/server.js +404 -92
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -697,7 +697,7 @@ var package_default;
|
|
|
697
697
|
var init_package = __esm(() => {
|
|
698
698
|
package_default = {
|
|
699
699
|
name: "@playcademy/sandbox",
|
|
700
|
-
version: "0.12.2-beta.
|
|
700
|
+
version: "0.12.2-beta.4",
|
|
701
701
|
description: "Local development server for Playcademy game development",
|
|
702
702
|
type: "module",
|
|
703
703
|
exports: {
|
|
@@ -80045,6 +80045,9 @@ function normalizedIdentityCase2(value) {
|
|
|
80045
80045
|
function frameworkAliasKey2(value) {
|
|
80046
80046
|
return value.normalize("NFKC").toLocaleLowerCase("en-US").replace(/[^a-z0-9]+/g, "");
|
|
80047
80047
|
}
|
|
80048
|
+
function isUuidShaped2(value) {
|
|
80049
|
+
return UUID_PATTERN2.test(value.trim());
|
|
80050
|
+
}
|
|
80048
80051
|
function isCommonCoreMathIdentifier2(identifier) {
|
|
80049
80052
|
const canonical = normalizedIdentityCase2(normalizedWhitespace2(identifier));
|
|
80050
80053
|
return canonical.startsWith("CCSS.MATH.") || /^(?:K|[1-8])\.(?:CC|OA|NBT|NF|MD|RP|NS|EE|F|G|SP)\./.test(canonical) || /^(?:HS)?(?:N|A|F|G|S)-[A-Z]+\./.test(canonical) || /^MP\.?\d/.test(canonical);
|
|
@@ -80076,6 +80079,14 @@ function canonicalIdentifier2(framework, authoredIdentifier) {
|
|
|
80076
80079
|
}
|
|
80077
80080
|
return identifier;
|
|
80078
80081
|
}
|
|
80082
|
+
function readableAlignmentIdentifier2(alignment) {
|
|
80083
|
+
const identifier = alignment.identifier?.trim() ?? "";
|
|
80084
|
+
if (identifier && !isUuidShaped2(identifier)) {
|
|
80085
|
+
return identifier;
|
|
80086
|
+
}
|
|
80087
|
+
const id = alignment.id.trim();
|
|
80088
|
+
return id && !isUuidShaped2(id) ? id : "";
|
|
80089
|
+
}
|
|
80079
80090
|
function canonicalAssessmentStandardRef2(standard) {
|
|
80080
80091
|
const authoredFramework = normalizedWhitespace2(standard.framework);
|
|
80081
80092
|
const framework = canonicalFramework2(authoredFramework, standard.identifier);
|
|
@@ -80182,7 +80193,23 @@ function qtiStandardsFromMetadata(metadata2) {
|
|
|
80182
80193
|
standards.push(...qtiAlignmentStandardsFromMetadata2(metadata2));
|
|
80183
80194
|
return dedupeStandards2(standards);
|
|
80184
80195
|
}
|
|
80185
|
-
|
|
80196
|
+
function qtiAssessmentStandardRefsFromMetadata2(metadata2) {
|
|
80197
|
+
const refs = new Map;
|
|
80198
|
+
for (const alignment of qtiAlignmentStandardsFromMetadata2(metadata2)) {
|
|
80199
|
+
const identifier = readableAlignmentIdentifier2(alignment);
|
|
80200
|
+
if (identifier) {
|
|
80201
|
+
const canonical = canonicalAssessmentStandardRef2({
|
|
80202
|
+
framework: alignment.source,
|
|
80203
|
+
identifier
|
|
80204
|
+
});
|
|
80205
|
+
if (canonical.framework && canonical.identifier) {
|
|
80206
|
+
refs.set(assessmentStandardRefKey2(canonical), canonical);
|
|
80207
|
+
}
|
|
80208
|
+
}
|
|
80209
|
+
}
|
|
80210
|
+
return [...refs.values()];
|
|
80211
|
+
}
|
|
80212
|
+
var EVENT_HANDLER_ATTRIBUTE, SCRIPT_SCHEMES2, DATA_RASTER_IMAGE_PATTERN2, QTI_MATHML_ALLOWED_TAGS2, metadataSymbol2, parser2, SUPPORTED_QTI_INTERACTION_TYPES2, URL_ATTRIBUTES3, IMAGE_URL_ATTRIBUTES3, INTERACTION_TYPES2, XML_NAMED_ENTITIES, EMPHASIS_TAGS2, STRONG_TAGS2, CONTENT_SKIPPED_TAGS2, BLOCK_CONTENT_KINDS2, GAP_MATCH_TOKEN_TAGS2, STAGE_GRAPHIC_EXCLUDED_CONTAINERS2, BLANK_SENTINEL2 = "", MARKED_BLANK_PATTERN2, MARKED_BLANK_GLOBAL_PATTERN2, REGION_SHAPE_COORDS, QTI_NAMESPACE = "http://www.imsglobal.org/xsd/imsqtiasi_v3p0", QTI_AUTHORING_BLANK_MARKER = "___", MATCH_CORRECT_TEMPLATE2 = "http://www.imsglobal.org/question/qti_v3p0/rptemplates/match_correct", MAP_RESPONSE_TEMPLATE2 = "https://purl.imsglobal.org/spec/qti/v3p0/rptemplates/map_response", MAP_RESPONSE_TEMPLATES2, RESPONSE_PROCESSING_TEMPLATES2, GLOBAL_BODY_ATTRIBUTES, BODY_ELEMENT_ATTRIBUTES, MATHML_ATTRIBUTES, VOID_BODY_ELEMENTS2, URL_ATTRIBUTES22, IMAGE_URL_ATTRIBUTES22, INTERACTION_ATTRIBUTES, CHOICE_ATTRIBUTES, CHOICE_ELEMENT_ATTRIBUTES, VOID_CHOICE_ELEMENTS2, NUMERIC_COMPARISON_ATTRIBUTES2, RESPONSE_CARDINALITIES2, RESPONSE_BASE_TYPES2, BLANK_SENTINEL22 = "", QTI_FLOAT_VALUE, PLAYABLE_POINT_VALUE, POINT_INTERACTION_TYPES2, INDEPENDENT_PROCESSING_TAGS2, PRIVATE_PLAYABLE_KEYS2, trimmedNonEmptyString2, finiteNumber22, positiveFiniteNumber2, IDENTIFIER_VALUE2, DIRECTED_PAIR_VALUE2, scoringMatchComparisonSchema2, scoringNumericEqualComparisonSchema2, scoringNumericEqualRoundedComparisonSchema2, scoringRuleSchema2, scoringRulesSchema2, scoringItemSchema2, assessmentScoringArtifactItemsSchema2, assessmentScoringArtifactSchema2, SUPPORTED_BASE_TYPES2, SUPPORTED_CARDINALITIES2, MATCH_CORRECT_TEMPLATES2, QtiScoringArtifactValidationError, UUID_PATTERN2, COMMON_CORE_MATH_FRAMEWORK_ALIASES2, COMMON_CORE_ELA_FRAMEWORK_ALIASES2, COMMON_CORE_FRAMEWORK_ALIASES2;
|
|
80186
80213
|
var init_qti = __esm(() => {
|
|
80187
80214
|
init_timeback4();
|
|
80188
80215
|
init_timeback4();
|
|
@@ -80544,6 +80571,7 @@ var init_qti = __esm(() => {
|
|
|
80544
80571
|
this.name = "QtiScoringArtifactValidationError";
|
|
80545
80572
|
}
|
|
80546
80573
|
};
|
|
80574
|
+
UUID_PATTERN2 = /^(?:urn:uuid:)?\{?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}?$/i;
|
|
80547
80575
|
COMMON_CORE_MATH_FRAMEWORK_ALIASES2 = new Set([
|
|
80548
80576
|
"ccssm",
|
|
80549
80577
|
"ccssmath",
|
|
@@ -80753,7 +80781,7 @@ function pruneUnreadableStandards(standards) {
|
|
|
80753
80781
|
return standards.filter(hasReadableStandardLabel);
|
|
80754
80782
|
}
|
|
80755
80783
|
function parseQuestionIndex(title) {
|
|
80756
|
-
const match = title
|
|
80784
|
+
const match = title?.match(/\bquestion[\s_-]*(\d+)\b/i);
|
|
80757
80785
|
const index = match ? Number(match[1]) : Number.NaN;
|
|
80758
80786
|
return Number.isFinite(index) && index > 0 ? index : null;
|
|
80759
80787
|
}
|
|
@@ -80787,14 +80815,15 @@ function qtiQuestionsByIndex(qtiQuestions) {
|
|
|
80787
80815
|
}
|
|
80788
80816
|
return questionsByIndex;
|
|
80789
80817
|
}
|
|
80818
|
+
function normalizedQuestionScore(score, scorePercentile) {
|
|
80819
|
+
const rawValue = numberField(score) ?? numberField(scorePercentile);
|
|
80820
|
+
return rawValue !== null && rawValue > 1 ? rawValue / 100 : rawValue;
|
|
80821
|
+
}
|
|
80790
80822
|
function getQuestionCorrectness(result) {
|
|
80791
80823
|
if (!result) {
|
|
80792
80824
|
return "unknown";
|
|
80793
80825
|
}
|
|
80794
|
-
const
|
|
80795
|
-
const scorePercentile = numberField(result.scorePercentile);
|
|
80796
|
-
const rawValue = score ?? scorePercentile;
|
|
80797
|
-
const value = rawValue !== null && rawValue > 1 ? rawValue / 100 : rawValue;
|
|
80826
|
+
const value = normalizedQuestionScore(result.score, result.scorePercentile);
|
|
80798
80827
|
if (value === null) {
|
|
80799
80828
|
return "unknown";
|
|
80800
80829
|
}
|
|
@@ -81058,10 +81087,71 @@ var init_timeback_mastery_completion_util = __esm(async () => {
|
|
|
81058
81087
|
await init_errors8();
|
|
81059
81088
|
});
|
|
81060
81089
|
|
|
81090
|
+
// ../api-core/src/utils/bounded-map.util.ts
|
|
81091
|
+
function setBounded(map, key, value, maxSize) {
|
|
81092
|
+
map.delete(key);
|
|
81093
|
+
if (map.size >= maxSize) {
|
|
81094
|
+
const oldestKey = map.keys().next().value;
|
|
81095
|
+
if (oldestKey !== undefined) {
|
|
81096
|
+
map.delete(oldestKey);
|
|
81097
|
+
}
|
|
81098
|
+
}
|
|
81099
|
+
map.set(key, value);
|
|
81100
|
+
}
|
|
81101
|
+
|
|
81102
|
+
// ../api-core/src/utils/timeback-qti-test-lookup.util.ts
|
|
81103
|
+
async function resolveQtiTestId(client, testName, parentLineItem) {
|
|
81104
|
+
const cacheKey = `${parentLineItem.sourcedId ?? ""}:${testName}`;
|
|
81105
|
+
const cached = cache.get(cacheKey);
|
|
81106
|
+
if (cached) {
|
|
81107
|
+
setBounded(cache, cacheKey, cached, MAX_CACHE_SIZE);
|
|
81108
|
+
return cached;
|
|
81109
|
+
}
|
|
81110
|
+
let resource = null;
|
|
81111
|
+
const resourceId = sourceIdFromRef(parentLineItem.componentResource);
|
|
81112
|
+
if (resourceId) {
|
|
81113
|
+
resource = await client.api.oneroster.resources.get(resourceId).catch(() => null);
|
|
81114
|
+
}
|
|
81115
|
+
for (const candidate of qtiIdCandidates(parentLineItem, resource)) {
|
|
81116
|
+
const test = await client.qtiApi.assessmentTests.get(candidate).catch(() => null);
|
|
81117
|
+
if (test?.identifier) {
|
|
81118
|
+
setBounded(cache, cacheKey, test.identifier, MAX_CACHE_SIZE);
|
|
81119
|
+
return test.identifier;
|
|
81120
|
+
}
|
|
81121
|
+
}
|
|
81122
|
+
const normalizedTestName = testName.trim();
|
|
81123
|
+
if (!normalizedTestName) {
|
|
81124
|
+
return "";
|
|
81125
|
+
}
|
|
81126
|
+
for (let page = 1;page <= TITLE_LOOKUP_MAX_PAGES; page++) {
|
|
81127
|
+
const list = await client.qtiApi.assessmentTests.list({
|
|
81128
|
+
query: normalizedTestName,
|
|
81129
|
+
limit: TITLE_LOOKUP_LIMIT,
|
|
81130
|
+
page,
|
|
81131
|
+
sort: "title",
|
|
81132
|
+
order: "asc"
|
|
81133
|
+
}).catch(() => null);
|
|
81134
|
+
const qtiTestId = findQtiAssessmentTestIdByTitle(list?.items ?? [], normalizedTestName);
|
|
81135
|
+
if (qtiTestId) {
|
|
81136
|
+
setBounded(cache, cacheKey, qtiTestId, MAX_CACHE_SIZE);
|
|
81137
|
+
return qtiTestId;
|
|
81138
|
+
}
|
|
81139
|
+
const itemCount = list?.items?.length ?? 0;
|
|
81140
|
+
if (!list || itemCount < TITLE_LOOKUP_LIMIT || typeof list.pages === "number" && page >= list.pages) {
|
|
81141
|
+
break;
|
|
81142
|
+
}
|
|
81143
|
+
}
|
|
81144
|
+
return "";
|
|
81145
|
+
}
|
|
81146
|
+
var MAX_CACHE_SIZE = 500, TITLE_LOOKUP_LIMIT = 50, TITLE_LOOKUP_MAX_PAGES = 5, cache;
|
|
81147
|
+
var init_timeback_qti_test_lookup_util = __esm(() => {
|
|
81148
|
+
init_timeback_grade_level_results_util();
|
|
81149
|
+
cache = new Map;
|
|
81150
|
+
});
|
|
81151
|
+
|
|
81061
81152
|
// ../api-core/src/services/timeback-admin.service.ts
|
|
81062
81153
|
class TimebackAdminService {
|
|
81063
81154
|
deps;
|
|
81064
|
-
qtiTestIdCache = new Map;
|
|
81065
81155
|
static XP_PRECISION_FACTOR = 10;
|
|
81066
81156
|
static RECENT_ACTIVITY_LIMIT = 20;
|
|
81067
81157
|
static MAX_STUDENT_ACTIVITY_LIMIT = 200;
|
|
@@ -81076,9 +81166,6 @@ class TimebackAdminService {
|
|
|
81076
81166
|
static GAME_METRICS_FETCH_TIMEOUT_MS = 1e4;
|
|
81077
81167
|
static GAME_METRICS_LIST_FETCH_TIMEOUT_MS = 3000;
|
|
81078
81168
|
static GAME_METRICS_RUN_IDS_PER_REQUEST = 50;
|
|
81079
|
-
static MAX_QTI_TEST_ID_CACHE_SIZE = 500;
|
|
81080
|
-
static QTI_TEST_TITLE_LOOKUP_LIMIT = 50;
|
|
81081
|
-
static QTI_TEST_TITLE_LOOKUP_MAX_PAGES = 5;
|
|
81082
81169
|
constructor(deps) {
|
|
81083
81170
|
this.deps = deps;
|
|
81084
81171
|
}
|
|
@@ -81938,68 +82025,6 @@ class TimebackAdminService {
|
|
|
81938
82025
|
return { activities: activitiesWithGameMetrics, hasMore };
|
|
81939
82026
|
});
|
|
81940
82027
|
}
|
|
81941
|
-
rememberQtiTestId(cacheKey, qtiTestId) {
|
|
81942
|
-
if (!qtiTestId) {
|
|
81943
|
-
this.qtiTestIdCache.delete(cacheKey);
|
|
81944
|
-
return;
|
|
81945
|
-
}
|
|
81946
|
-
if (this.qtiTestIdCache.has(cacheKey)) {
|
|
81947
|
-
this.qtiTestIdCache.delete(cacheKey);
|
|
81948
|
-
} else if (this.qtiTestIdCache.size >= TimebackAdminService.MAX_QTI_TEST_ID_CACHE_SIZE) {
|
|
81949
|
-
const oldestKey = this.qtiTestIdCache.keys().next().value;
|
|
81950
|
-
if (oldestKey !== undefined) {
|
|
81951
|
-
this.qtiTestIdCache.delete(oldestKey);
|
|
81952
|
-
}
|
|
81953
|
-
}
|
|
81954
|
-
this.qtiTestIdCache.set(cacheKey, qtiTestId);
|
|
81955
|
-
}
|
|
81956
|
-
async resolveQtiTestIdForGradeLevelResult(client, testName, parentLineItem) {
|
|
81957
|
-
const cacheKey = `${parentLineItem.sourcedId ?? ""}:${testName}`;
|
|
81958
|
-
const cachedQtiTestId = cacheKey ? this.qtiTestIdCache.get(cacheKey) : undefined;
|
|
81959
|
-
if (cachedQtiTestId) {
|
|
81960
|
-
this.rememberQtiTestId(cacheKey, cachedQtiTestId);
|
|
81961
|
-
return cachedQtiTestId;
|
|
81962
|
-
}
|
|
81963
|
-
let resource = null;
|
|
81964
|
-
const resourceId = sourceIdFromRef(parentLineItem.componentResource);
|
|
81965
|
-
if (resourceId) {
|
|
81966
|
-
resource = await client.api.oneroster.resources.get(resourceId).catch(() => null);
|
|
81967
|
-
}
|
|
81968
|
-
for (const candidate of qtiIdCandidates(parentLineItem, resource)) {
|
|
81969
|
-
const test = await client.qtiApi.assessmentTests.get(candidate).catch(() => null);
|
|
81970
|
-
if (test?.identifier) {
|
|
81971
|
-
if (cacheKey) {
|
|
81972
|
-
this.rememberQtiTestId(cacheKey, test.identifier);
|
|
81973
|
-
}
|
|
81974
|
-
return test.identifier;
|
|
81975
|
-
}
|
|
81976
|
-
}
|
|
81977
|
-
const normalizedTestName = testName.trim();
|
|
81978
|
-
if (!normalizedTestName) {
|
|
81979
|
-
return "";
|
|
81980
|
-
}
|
|
81981
|
-
for (let page = 1;page <= TimebackAdminService.QTI_TEST_TITLE_LOOKUP_MAX_PAGES; page++) {
|
|
81982
|
-
const list = await client.qtiApi.assessmentTests.list({
|
|
81983
|
-
query: normalizedTestName,
|
|
81984
|
-
limit: TimebackAdminService.QTI_TEST_TITLE_LOOKUP_LIMIT,
|
|
81985
|
-
page,
|
|
81986
|
-
sort: "title",
|
|
81987
|
-
order: "asc"
|
|
81988
|
-
}).catch(() => null);
|
|
81989
|
-
const qtiTestId = findQtiAssessmentTestIdByTitle(list?.items ?? [], normalizedTestName);
|
|
81990
|
-
if (qtiTestId) {
|
|
81991
|
-
if (cacheKey) {
|
|
81992
|
-
this.rememberQtiTestId(cacheKey, qtiTestId);
|
|
81993
|
-
}
|
|
81994
|
-
return qtiTestId;
|
|
81995
|
-
}
|
|
81996
|
-
const itemCount = list?.items?.length ?? 0;
|
|
81997
|
-
if (!list || itemCount < TimebackAdminService.QTI_TEST_TITLE_LOOKUP_LIMIT || typeof list.pages === "number" && page >= list.pages) {
|
|
81998
|
-
break;
|
|
81999
|
-
}
|
|
82000
|
-
}
|
|
82001
|
-
return "";
|
|
82002
|
-
}
|
|
82003
82028
|
async getGradeLevelTestResults(user, options) {
|
|
82004
82029
|
return this.withClientTelemetry(async () => {
|
|
82005
82030
|
const { gameId, courseId, studentId } = options;
|
|
@@ -82102,7 +82127,7 @@ class TimebackAdminService {
|
|
|
82102
82127
|
}).then((page) => page.data) : [];
|
|
82103
82128
|
const latestResultsByLineItem = latestAssessmentResultsByLineItem(questionResults);
|
|
82104
82129
|
const testName = resolveGradeLevelTestName(parentResult, parentLineItem);
|
|
82105
|
-
const qtiTestId = await
|
|
82130
|
+
const qtiTestId = await resolveQtiTestId(client, testName, parentLineItem);
|
|
82106
82131
|
const qtiQuestions = qtiTestId ? await client.qtiApi.assessmentTests.getQuestions(qtiTestId).then((response) => response.questions ?? []).catch(() => []) : [];
|
|
82107
82132
|
const questions = mapGradeLevelReviewQuestions({
|
|
82108
82133
|
questionLineItems,
|
|
@@ -82796,6 +82821,7 @@ var init_timeback_admin_service = __esm(async () => {
|
|
|
82796
82821
|
init_timeback_discrepancy_queue_util();
|
|
82797
82822
|
init_timeback_game_metrics_comparison_util();
|
|
82798
82823
|
init_timeback_grade_level_results_util();
|
|
82824
|
+
init_timeback_qti_test_lookup_util();
|
|
82799
82825
|
init_timeback_util();
|
|
82800
82826
|
await __promiseAll([
|
|
82801
82827
|
init_dist5(),
|
|
@@ -90802,6 +90828,128 @@ var init_timeback_create_integration_util = __esm(() => {
|
|
|
90802
90828
|
init_timeback_util();
|
|
90803
90829
|
});
|
|
90804
90830
|
|
|
90831
|
+
// ../api-core/src/utils/timeback-hole-filling-context.util.ts
|
|
90832
|
+
function buildAssignmentResultWhere(options) {
|
|
90833
|
+
return {
|
|
90834
|
+
status: "active",
|
|
90835
|
+
"student.sourcedId": options.studentId,
|
|
90836
|
+
"metadata.resultType": options.resultType,
|
|
90837
|
+
"metadata.assignmentId": options.assignmentId
|
|
90838
|
+
};
|
|
90839
|
+
}
|
|
90840
|
+
function assignmentIdOf(record) {
|
|
90841
|
+
const value = metadataOf(record).assignmentId;
|
|
90842
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
90843
|
+
return String(value);
|
|
90844
|
+
}
|
|
90845
|
+
const text = stringField3(value).trim();
|
|
90846
|
+
return text || undefined;
|
|
90847
|
+
}
|
|
90848
|
+
function toTriggeringResult(result) {
|
|
90849
|
+
const metadata2 = metadataOf(result);
|
|
90850
|
+
const testType = normalizeTestType(metadata2.testType);
|
|
90851
|
+
const assignmentId = assignmentIdOf(result);
|
|
90852
|
+
const lineItemId = sourceIdFromRef(result.assessmentLineItem);
|
|
90853
|
+
if (!assignmentId || !lineItemId || result.scoreStatus !== SCORE_STATUS2.fullyGraded || !isAllowedGradeLevelTestType(testType)) {
|
|
90854
|
+
return null;
|
|
90855
|
+
}
|
|
90856
|
+
return {
|
|
90857
|
+
resultId: result.sourcedId ?? "",
|
|
90858
|
+
assignmentId,
|
|
90859
|
+
lineItemId,
|
|
90860
|
+
testName: stringField3(metadata2.testName) || lineItemId,
|
|
90861
|
+
testType,
|
|
90862
|
+
score: numberField(result.score),
|
|
90863
|
+
scoreDate: result.scoreDate ?? null,
|
|
90864
|
+
totalQuestions: numberField(metadata2.totalQuestions),
|
|
90865
|
+
correctQuestions: numberField(metadata2.correctQuestions)
|
|
90866
|
+
};
|
|
90867
|
+
}
|
|
90868
|
+
function isMissed(resultText, score, scorePercentile) {
|
|
90869
|
+
const marking = stringField3(resultText).trim().toLowerCase();
|
|
90870
|
+
if (marking) {
|
|
90871
|
+
return marking !== "correct";
|
|
90872
|
+
}
|
|
90873
|
+
const value = normalizedQuestionScore(score, scorePercentile);
|
|
90874
|
+
return value !== null && value < 1;
|
|
90875
|
+
}
|
|
90876
|
+
function missedQuestionsFromInlineResponses(result) {
|
|
90877
|
+
const responses = metadataOf(result).responses;
|
|
90878
|
+
const questions = isRecord6(responses) && Array.isArray(responses.questions) ? responses.questions : undefined;
|
|
90879
|
+
if (!questions?.length) {
|
|
90880
|
+
return;
|
|
90881
|
+
}
|
|
90882
|
+
return questions.flatMap((question) => {
|
|
90883
|
+
if (!isRecord6(question) || !isMissed(question.result, question.score)) {
|
|
90884
|
+
return [];
|
|
90885
|
+
}
|
|
90886
|
+
const questionNumber = numberField(question.questionNumber);
|
|
90887
|
+
const qtiItemId = stringField3(question.questionQtiIdentifier).trim();
|
|
90888
|
+
return questionNumber === null ? [] : [{ questionNumber, ...qtiItemId && { qtiItemId } }];
|
|
90889
|
+
});
|
|
90890
|
+
}
|
|
90891
|
+
function questionNumberFromLineItemId(lineItemId) {
|
|
90892
|
+
const match = lineItemId.match(/_q(\d+)$/);
|
|
90893
|
+
const value = match ? Number(match[1]) : Number.NaN;
|
|
90894
|
+
return Number.isInteger(value) && value > 0 ? value : null;
|
|
90895
|
+
}
|
|
90896
|
+
function missedQuestionsFromQuestionResults(questionResults) {
|
|
90897
|
+
return [...latestAssessmentResultsByLineItem(questionResults)].flatMap(([lineItemId, result]) => {
|
|
90898
|
+
const questionNumber = questionNumberFromLineItemId(lineItemId);
|
|
90899
|
+
const responses = metadataOf(result).responses;
|
|
90900
|
+
const response = isRecord6(responses) && isRecord6(responses.response) ? responses.response : undefined;
|
|
90901
|
+
if (questionNumber === null || !isMissed(response?.result, result.score, result.scorePercentile)) {
|
|
90902
|
+
return [];
|
|
90903
|
+
}
|
|
90904
|
+
const qtiItemId = stringField3(response?.questionQtiIdentifier).trim();
|
|
90905
|
+
return [{ questionNumber, ...qtiItemId && { qtiItemId } }];
|
|
90906
|
+
}).toSorted((a, b) => a.questionNumber - b.questionNumber);
|
|
90907
|
+
}
|
|
90908
|
+
function qtiItemIdForQuestionNumber(qtiQuestions) {
|
|
90909
|
+
const byNumber = qtiQuestionsByIndex(qtiQuestions);
|
|
90910
|
+
return (questionNumber) => {
|
|
90911
|
+
const question = byNumber.get(questionNumber) ?? qtiQuestions[questionNumber - 1];
|
|
90912
|
+
return question ? question.question.identifier || question.reference.identifier : undefined;
|
|
90913
|
+
};
|
|
90914
|
+
}
|
|
90915
|
+
function groupMissedStandards(missed, standardsByItemId, itemIdFor) {
|
|
90916
|
+
const byKey = new Map;
|
|
90917
|
+
const untagged = [];
|
|
90918
|
+
for (const question of missed) {
|
|
90919
|
+
const itemId = question.qtiItemId ?? itemIdFor(question.questionNumber);
|
|
90920
|
+
const standards = itemId ? standardsByItemId.get(itemId) ?? [] : [];
|
|
90921
|
+
if (!standards.length) {
|
|
90922
|
+
untagged.push(question.questionNumber);
|
|
90923
|
+
}
|
|
90924
|
+
for (const standard of standards) {
|
|
90925
|
+
const key = `${standard.framework}\x00${standard.identifier}`;
|
|
90926
|
+
const entry = byKey.get(key) ?? {
|
|
90927
|
+
framework: standard.framework,
|
|
90928
|
+
code: standard.identifier,
|
|
90929
|
+
questionNumbers: [],
|
|
90930
|
+
qtiItemIds: []
|
|
90931
|
+
};
|
|
90932
|
+
entry.questionNumbers.push(question.questionNumber);
|
|
90933
|
+
if (itemId && !entry.qtiItemIds.includes(itemId)) {
|
|
90934
|
+
entry.qtiItemIds.push(itemId);
|
|
90935
|
+
}
|
|
90936
|
+
byKey.set(key, entry);
|
|
90937
|
+
}
|
|
90938
|
+
}
|
|
90939
|
+
return {
|
|
90940
|
+
missedStandards: [...byKey.values()].toSorted((a, b) => b.questionNumbers.length - a.questionNumbers.length || a.code.localeCompare(b.code, undefined, { numeric: true })),
|
|
90941
|
+
untaggedQuestionNumbers: untagged.toSorted((a, b) => a - b)
|
|
90942
|
+
};
|
|
90943
|
+
}
|
|
90944
|
+
function standardsFromQtiItem(item) {
|
|
90945
|
+
return qtiAssessmentStandardRefsFromMetadata2(metadataOf(item));
|
|
90946
|
+
}
|
|
90947
|
+
var init_timeback_hole_filling_context_util = __esm(() => {
|
|
90948
|
+
init_constants2();
|
|
90949
|
+
init_qti();
|
|
90950
|
+
init_timeback_grade_level_results_util();
|
|
90951
|
+
});
|
|
90952
|
+
|
|
90805
90953
|
// ../api-core/src/utils/timeback-promotion.util.ts
|
|
90806
90954
|
async function promoteCompletedCourse({
|
|
90807
90955
|
db,
|
|
@@ -90931,14 +91079,20 @@ var init_timeback_service = __esm(async () => {
|
|
|
90931
91079
|
init_helpers_index();
|
|
90932
91080
|
init_tables_index();
|
|
90933
91081
|
init_spans();
|
|
91082
|
+
init_constants2();
|
|
90934
91083
|
init_types2();
|
|
90935
91084
|
init_errors2();
|
|
90936
91085
|
init_timeback_create_integration_util();
|
|
91086
|
+
init_timeback_grade_level_results_util();
|
|
91087
|
+
init_timeback_hole_filling_context_util();
|
|
90937
91088
|
init_timeback_promotion_util();
|
|
91089
|
+
init_timeback_qti_test_lookup_util();
|
|
90938
91090
|
init_timeback_removed_integration_util();
|
|
90939
91091
|
init_timeback_util();
|
|
90940
91092
|
await init_dist5();
|
|
90941
91093
|
TimebackService = class TimebackService {
|
|
91094
|
+
static HOLE_FILLING_CONTEXT_TTL_MS = 10 * 60 * 1000;
|
|
91095
|
+
static HOLE_FILLING_CONTEXT_CACHE_SIZE = 1000;
|
|
90942
91096
|
static HEARTBEAT_DEDUPE_TTL_MS = 5 * 60 * 1000;
|
|
90943
91097
|
static processedHeartbeatWindows = new Map;
|
|
90944
91098
|
static inFlightHeartbeatWindows = new Map;
|
|
@@ -90946,6 +91100,7 @@ var init_timeback_service = __esm(async () => {
|
|
|
90946
91100
|
static COMPLETION_IN_FLIGHT_SETTLE_DELAY_MS = 400;
|
|
90947
91101
|
static COMPLETION_IN_FLIGHT_SETTLE_ATTEMPTS = 5;
|
|
90948
91102
|
deps;
|
|
91103
|
+
holeFillingContextCache = new Map;
|
|
90949
91104
|
static cleanHeartbeatDedupeCache(now = Date.now()) {
|
|
90950
91105
|
for (const [key, timestamp] of this.processedHeartbeatWindows) {
|
|
90951
91106
|
if (now - timestamp > this.HEARTBEAT_DEDUPE_TTL_MS) {
|
|
@@ -91319,19 +91474,163 @@ var init_timeback_service = __esm(async () => {
|
|
|
91319
91474
|
}
|
|
91320
91475
|
async getUserEnrollments(userId, gameId) {
|
|
91321
91476
|
return this.withClientTelemetry(async () => {
|
|
91322
|
-
const
|
|
91323
|
-
|
|
91324
|
-
|
|
91325
|
-
|
|
91326
|
-
|
|
91327
|
-
|
|
91477
|
+
const enrollments = await this.fetchEnrollments(await this.requireTimebackId(userId));
|
|
91478
|
+
return gameId ? filterEnrollmentsByGame(enrollments, gameId) : enrollments;
|
|
91479
|
+
});
|
|
91480
|
+
}
|
|
91481
|
+
async requireTimebackId(userId) {
|
|
91482
|
+
const userData = await this.deps.db.query.users.findFirst({
|
|
91483
|
+
where: eq(users.id, userId),
|
|
91484
|
+
columns: { id: true, timebackId: true }
|
|
91485
|
+
});
|
|
91486
|
+
if (!userData) {
|
|
91487
|
+
throw new NotFoundError("User", userId);
|
|
91488
|
+
}
|
|
91489
|
+
if (!userData.timebackId) {
|
|
91490
|
+
throw new NotFoundError("Timeback account not found for user");
|
|
91491
|
+
}
|
|
91492
|
+
return userData.timebackId;
|
|
91493
|
+
}
|
|
91494
|
+
async getHoleFillingContext(userId, gameId) {
|
|
91495
|
+
return this.withClientTelemetry(async () => {
|
|
91496
|
+
setAttribute("app.timeback.operation", "get_hole_filling_context");
|
|
91497
|
+
const studentId = await this.requireTimebackId(userId);
|
|
91498
|
+
const enrollments = filterEnrollmentsByGame(await this.fetchEnrollments(studentId), gameId);
|
|
91499
|
+
const holeFilling = enrollments.filter((enrollment) => enrollment.kind === HOLE_FILLING_GAME_TIMEBACK_INTEGRATION_KIND);
|
|
91500
|
+
setAttribute("app.timeback.hole_filling_enrollment_count", holeFilling.length);
|
|
91501
|
+
if (holeFilling.length === 0) {
|
|
91502
|
+
return { contexts: [] };
|
|
91328
91503
|
}
|
|
91329
|
-
|
|
91330
|
-
|
|
91504
|
+
const contexts = await Promise.all(holeFilling.map((enrollment) => this.buildHoleFillingContext(studentId, enrollment)));
|
|
91505
|
+
return { contexts };
|
|
91506
|
+
});
|
|
91507
|
+
}
|
|
91508
|
+
async buildHoleFillingContext(studentId, enrollment) {
|
|
91509
|
+
const client = this.requireClient();
|
|
91510
|
+
const grade = enrollment.grade;
|
|
91511
|
+
const subject = enrollment.subject;
|
|
91512
|
+
const enrollmentContext = {
|
|
91513
|
+
courseId: enrollment.courseId,
|
|
91514
|
+
baseCourseId: enrollment.baseCourseId ?? "",
|
|
91515
|
+
...enrollment.id && { enrollmentId: enrollment.id },
|
|
91516
|
+
grade,
|
|
91517
|
+
subject,
|
|
91518
|
+
beginDate: enrollment.beginDate ?? null
|
|
91519
|
+
};
|
|
91520
|
+
const stampedAssignmentId = assignmentIdOf({ metadata: enrollment.enrollmentMetadata });
|
|
91521
|
+
let parent = null;
|
|
91522
|
+
if (stampedAssignmentId) {
|
|
91523
|
+
const where = {
|
|
91524
|
+
...buildAssignmentResultWhere({
|
|
91525
|
+
studentId,
|
|
91526
|
+
assignmentId: stampedAssignmentId,
|
|
91527
|
+
resultType: "assessment"
|
|
91528
|
+
}),
|
|
91529
|
+
scoreStatus: SCORE_STATUS2.fullyGraded
|
|
91530
|
+
};
|
|
91531
|
+
const page = await client.api.oneroster.assessmentResults.list({
|
|
91532
|
+
limit: 1,
|
|
91533
|
+
where,
|
|
91534
|
+
sort: "scoreDate",
|
|
91535
|
+
orderBy: "desc"
|
|
91536
|
+
});
|
|
91537
|
+
parent = page.data[0] ?? null;
|
|
91538
|
+
}
|
|
91539
|
+
const summary = parent ? toTriggeringResult(parent) : null;
|
|
91540
|
+
let trigger = "not_stamped";
|
|
91541
|
+
if (stampedAssignmentId) {
|
|
91542
|
+
trigger = summary ? "found" : "not_found";
|
|
91543
|
+
}
|
|
91544
|
+
setAttributes({
|
|
91545
|
+
"app.timeback.hole_filling_trigger": trigger,
|
|
91546
|
+
"app.timeback.hole_filling_assignment_id": stampedAssignmentId
|
|
91547
|
+
});
|
|
91548
|
+
if (!parent || !summary) {
|
|
91549
|
+
return {
|
|
91550
|
+
enrollment: enrollmentContext,
|
|
91551
|
+
triggeringResult: null,
|
|
91552
|
+
missedStandards: [],
|
|
91553
|
+
untaggedMissedQuestionNumbers: []
|
|
91554
|
+
};
|
|
91555
|
+
}
|
|
91556
|
+
const cacheKey = `${enrollment.courseId}:${studentId}:${summary.resultId}`;
|
|
91557
|
+
const cached = this.holeFillingContextCache.get(cacheKey);
|
|
91558
|
+
if (cached && Date.now() - cached.at < TimebackService.HOLE_FILLING_CONTEXT_TTL_MS) {
|
|
91559
|
+
return { ...cached.context, enrollment: enrollmentContext };
|
|
91560
|
+
}
|
|
91561
|
+
const { context, complete } = await this.resolveMissedStandards(studentId, parent, summary, enrollmentContext);
|
|
91562
|
+
setAttribute("app.timeback.hole_filling_standards_complete", complete);
|
|
91563
|
+
if (complete) {
|
|
91564
|
+
setBounded(this.holeFillingContextCache, cacheKey, { at: Date.now(), context }, TimebackService.HOLE_FILLING_CONTEXT_CACHE_SIZE);
|
|
91565
|
+
}
|
|
91566
|
+
return context;
|
|
91567
|
+
}
|
|
91568
|
+
async resolveMissedStandards(studentId, parent, summary, enrollmentContext) {
|
|
91569
|
+
const client = this.requireClient();
|
|
91570
|
+
let complete = true;
|
|
91571
|
+
let missed = missedQuestionsFromInlineResponses(parent);
|
|
91572
|
+
if (!missed) {
|
|
91573
|
+
const where = buildAssignmentResultWhere({
|
|
91574
|
+
studentId,
|
|
91575
|
+
assignmentId: summary.assignmentId,
|
|
91576
|
+
resultType: "question"
|
|
91577
|
+
});
|
|
91578
|
+
const questionResults = await client.api.oneroster.assessmentResults.listAll({
|
|
91579
|
+
limit: 200,
|
|
91580
|
+
where,
|
|
91581
|
+
sort: "scoreDate",
|
|
91582
|
+
orderBy: "desc"
|
|
91583
|
+
});
|
|
91584
|
+
missed = missedQuestionsFromQuestionResults(questionResults);
|
|
91585
|
+
}
|
|
91586
|
+
let qtiTestId = "";
|
|
91587
|
+
let qtiQuestions = [];
|
|
91588
|
+
const standardsByItemId = new Map;
|
|
91589
|
+
if (missed.length > 0 && !client.hasQti) {
|
|
91590
|
+
complete = false;
|
|
91591
|
+
missed = missed.map(({ questionNumber }) => ({ questionNumber }));
|
|
91592
|
+
} else if (missed.some((question) => !question.qtiItemId)) {
|
|
91593
|
+
const lineItem = await client.api.oneroster.assessmentLineItems.get(summary.lineItemId).catch(() => null);
|
|
91594
|
+
qtiTestId = lineItem ? await resolveQtiTestId(client, resolveGradeLevelTestName(parent, lineItem), lineItem) : "";
|
|
91595
|
+
complete &&= Boolean(qtiTestId);
|
|
91596
|
+
if (qtiTestId) {
|
|
91597
|
+
qtiQuestions = await client.qtiApi.assessmentTests.getQuestions(qtiTestId).then((response) => response.questions ?? []).catch(() => {
|
|
91598
|
+
complete = false;
|
|
91599
|
+
return [];
|
|
91600
|
+
});
|
|
91601
|
+
for (const question of qtiQuestions) {
|
|
91602
|
+
const id = question.question.identifier || question.reference.identifier;
|
|
91603
|
+
if (id) {
|
|
91604
|
+
standardsByItemId.set(id, standardsFromQtiItem(question.question));
|
|
91605
|
+
}
|
|
91606
|
+
}
|
|
91331
91607
|
}
|
|
91332
|
-
|
|
91333
|
-
|
|
91608
|
+
}
|
|
91609
|
+
const itemIdsToFetch = [
|
|
91610
|
+
...new Set(missed.flatMap((question) => question.qtiItemId && !standardsByItemId.has(question.qtiItemId) ? [question.qtiItemId] : []))
|
|
91611
|
+
];
|
|
91612
|
+
const items = await Promise.all(itemIdsToFetch.map((id) => client.qtiApi.assessmentItems.get(id).then((item) => [id, standardsFromQtiItem(item)]).catch(() => {
|
|
91613
|
+
complete = false;
|
|
91614
|
+
return [id, []];
|
|
91615
|
+
})));
|
|
91616
|
+
for (const [id, standards] of items) {
|
|
91617
|
+
standardsByItemId.set(id, standards);
|
|
91618
|
+
}
|
|
91619
|
+
const { missedStandards, untaggedQuestionNumbers } = groupMissedStandards(missed, standardsByItemId, qtiItemIdForQuestionNumber(qtiQuestions));
|
|
91620
|
+
setAttributes({
|
|
91621
|
+
"app.timeback.hole_filling_missed_question_count": missed.length,
|
|
91622
|
+
"app.timeback.hole_filling_missed_standard_count": missedStandards.length,
|
|
91623
|
+
"app.timeback.hole_filling_untagged_question_count": untaggedQuestionNumbers.length
|
|
91334
91624
|
});
|
|
91625
|
+
return {
|
|
91626
|
+
context: {
|
|
91627
|
+
enrollment: enrollmentContext,
|
|
91628
|
+
triggeringResult: { ...summary, ...qtiTestId && { qtiTestId } },
|
|
91629
|
+
missedStandards,
|
|
91630
|
+
untaggedMissedQuestionNumbers: untaggedQuestionNumbers
|
|
91631
|
+
},
|
|
91632
|
+
complete
|
|
91633
|
+
};
|
|
91335
91634
|
}
|
|
91336
91635
|
async getUserDataByTimebackId(timebackId) {
|
|
91337
91636
|
return this.withClientTelemetry(async () => {
|
|
@@ -94891,33 +95190,33 @@ var init_auth_provider = __esm(async () => {
|
|
|
94891
95190
|
function createSandboxCacheProvider() {
|
|
94892
95191
|
return {
|
|
94893
95192
|
async get(key) {
|
|
94894
|
-
const entry =
|
|
95193
|
+
const entry = cache2.get(key);
|
|
94895
95194
|
if (!entry) {
|
|
94896
95195
|
return null;
|
|
94897
95196
|
}
|
|
94898
95197
|
if (entry.expiresAt < Date.now()) {
|
|
94899
|
-
|
|
95198
|
+
cache2.delete(key);
|
|
94900
95199
|
return null;
|
|
94901
95200
|
}
|
|
94902
95201
|
return entry.value;
|
|
94903
95202
|
},
|
|
94904
95203
|
async set(key, value, ttlSeconds = 300) {
|
|
94905
|
-
|
|
95204
|
+
cache2.set(key, {
|
|
94906
95205
|
value,
|
|
94907
95206
|
expiresAt: Date.now() + ttlSeconds * 1000
|
|
94908
95207
|
});
|
|
94909
95208
|
},
|
|
94910
95209
|
async delete(key) {
|
|
94911
|
-
|
|
95210
|
+
cache2.delete(key);
|
|
94912
95211
|
}
|
|
94913
95212
|
};
|
|
94914
95213
|
}
|
|
94915
95214
|
function clearSandboxCache() {
|
|
94916
|
-
|
|
95215
|
+
cache2.clear();
|
|
94917
95216
|
}
|
|
94918
|
-
var
|
|
95217
|
+
var cache2;
|
|
94919
95218
|
var init_cache_provider = __esm(() => {
|
|
94920
|
-
|
|
95219
|
+
cache2 = new Map;
|
|
94921
95220
|
});
|
|
94922
95221
|
|
|
94923
95222
|
// ../utils/src/keys.ts
|
|
@@ -151145,7 +151444,7 @@ function parseQtiLibraryParams(searchParams) {
|
|
|
151145
151444
|
limit
|
|
151146
151445
|
};
|
|
151147
151446
|
}
|
|
151148
|
-
var populateStudent, getUser, getUserEnrollments, getUserById, setupIntegration, getIntegrations, getRemovedIntegrations, createIntegration, getHoleFillingCourses, createHoleFillingCourse, updateIntegration, deactivateCourse, reactivateCourse, getIntegrationConfig, verifyIntegration, getConfig, deleteIntegrations, endActivity, heartbeat, advanceCourse, unenrollCourse, prepareRuntimeAssessment, startRuntimeAssessment, getRuntimeAssessment, getLatestRuntimeAssessment, resumeRuntimeAssessment, saveRuntimeAssessment, recordRuntimeAssessmentProgress, submitRuntimeAssessmentItem, submitRuntimeAssessment, finalizeRuntimeAssessment, exportRuntimeAssessmentFixture, getStudentXp, getStudentMastery, getStudentHighestGradeMastered, getRoster, getStudentOverview, getGameMetrics, getStudentActivity, getGradeLevelTestResults, getGradeLevelTestReview, getActivityDetail, listMetricDiscrepancies, verifyMetricDiscrepancy, grantXp, adjustTime, adjustMastery, reconcileMasteryForConfigChange, searchStudents, enrollStudent, unenrollStudent, reactivateEnrollment, listAssessments, createAssessment, attachExistingAssessments, updateAssessment, reviseAssessment, synchronizeReviewBank, reorderQuestions, removeAssessment, listQuestions, listQuestionLibrary, listTestLibrary, copyAssessment, createQuestion, updateQuestion, removeQuestion, timeback2;
|
|
151447
|
+
var populateStudent, getUser, getUserEnrollments, getUserHoleFillingContext, getUserById, setupIntegration, getIntegrations, getRemovedIntegrations, createIntegration, getHoleFillingCourses, createHoleFillingCourse, updateIntegration, deactivateCourse, reactivateCourse, getIntegrationConfig, verifyIntegration, getConfig, deleteIntegrations, endActivity, heartbeat, advanceCourse, unenrollCourse, prepareRuntimeAssessment, startRuntimeAssessment, getRuntimeAssessment, getLatestRuntimeAssessment, resumeRuntimeAssessment, saveRuntimeAssessment, recordRuntimeAssessmentProgress, submitRuntimeAssessmentItem, submitRuntimeAssessment, finalizeRuntimeAssessment, exportRuntimeAssessmentFixture, getStudentXp, getStudentMastery, getStudentHighestGradeMastered, getRoster, getStudentOverview, getGameMetrics, getStudentActivity, getGradeLevelTestResults, getGradeLevelTestReview, getActivityDetail, listMetricDiscrepancies, verifyMetricDiscrepancy, grantXp, adjustTime, adjustMastery, reconcileMasteryForConfigChange, searchStudents, enrollStudent, unenrollStudent, reactivateEnrollment, listAssessments, createAssessment, attachExistingAssessments, updateAssessment, reviseAssessment, synchronizeReviewBank, reorderQuestions, removeAssessment, listQuestions, listQuestionLibrary, listTestLibrary, copyAssessment, createQuestion, updateQuestion, removeQuestion, timeback2;
|
|
151149
151448
|
var init_timeback_controller = __esm(() => {
|
|
151150
151449
|
init_esm();
|
|
151151
151450
|
init_src();
|
|
@@ -151171,6 +151470,12 @@ var init_timeback_controller = __esm(() => {
|
|
|
151171
151470
|
});
|
|
151172
151471
|
getUser = requireNonAnonymous(async (ctx) => ctx.services.timeback.getUserData(ctx.user.id, ctx.gameId));
|
|
151173
151472
|
getUserEnrollments = requireNonAnonymous(async (ctx) => ctx.services.timeback.getUserEnrollments(ctx.user.id, ctx.gameId));
|
|
151473
|
+
getUserHoleFillingContext = requireNonAnonymous(async (ctx) => {
|
|
151474
|
+
if (!ctx.gameId) {
|
|
151475
|
+
throw ApiError.forbidden("Hole-filling context requires a game token");
|
|
151476
|
+
}
|
|
151477
|
+
return ctx.services.timeback.getHoleFillingContext(ctx.user.id, ctx.gameId);
|
|
151478
|
+
});
|
|
151174
151479
|
getUserById = requireNonAnonymous(async (ctx) => {
|
|
151175
151480
|
const timebackId = ctx.params.timebackId;
|
|
151176
151481
|
if (!timebackId) {
|
|
@@ -152024,6 +152329,7 @@ var init_timeback_controller = __esm(() => {
|
|
|
152024
152329
|
populateStudent,
|
|
152025
152330
|
getUser,
|
|
152026
152331
|
getUserEnrollments,
|
|
152332
|
+
getUserHoleFillingContext,
|
|
152027
152333
|
getUserById,
|
|
152028
152334
|
setupIntegration,
|
|
152029
152335
|
getIntegrations,
|
|
@@ -153086,6 +153392,12 @@ var init_timeback8 = __esm(async () => {
|
|
|
153086
153392
|
}
|
|
153087
153393
|
return handle2(timeback2.getUserEnrollments)(c);
|
|
153088
153394
|
});
|
|
153395
|
+
timebackRouter.get("/user/hole-filling-context", async (c) => {
|
|
153396
|
+
if (shouldMockTimeback()) {
|
|
153397
|
+
return c.json({ contexts: [] });
|
|
153398
|
+
}
|
|
153399
|
+
return handle2(timeback2.getUserHoleFillingContext)(c);
|
|
153400
|
+
});
|
|
153089
153401
|
timebackRouter.get("/user/:timebackId", async (c) => {
|
|
153090
153402
|
const user = c.get("user");
|
|
153091
153403
|
if (!user) {
|