@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/cli.js
CHANGED
|
@@ -2517,7 +2517,7 @@ var package_default;
|
|
|
2517
2517
|
var init_package = __esm(() => {
|
|
2518
2518
|
package_default = {
|
|
2519
2519
|
name: "@playcademy/sandbox",
|
|
2520
|
-
version: "0.12.2-beta.
|
|
2520
|
+
version: "0.12.2-beta.4",
|
|
2521
2521
|
description: "Local development server for Playcademy game development",
|
|
2522
2522
|
type: "module",
|
|
2523
2523
|
exports: {
|
|
@@ -81865,6 +81865,9 @@ function normalizedIdentityCase2(value) {
|
|
|
81865
81865
|
function frameworkAliasKey2(value) {
|
|
81866
81866
|
return value.normalize("NFKC").toLocaleLowerCase("en-US").replace(/[^a-z0-9]+/g, "");
|
|
81867
81867
|
}
|
|
81868
|
+
function isUuidShaped2(value) {
|
|
81869
|
+
return UUID_PATTERN2.test(value.trim());
|
|
81870
|
+
}
|
|
81868
81871
|
function isCommonCoreMathIdentifier2(identifier) {
|
|
81869
81872
|
const canonical = normalizedIdentityCase2(normalizedWhitespace2(identifier));
|
|
81870
81873
|
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);
|
|
@@ -81896,6 +81899,14 @@ function canonicalIdentifier2(framework, authoredIdentifier) {
|
|
|
81896
81899
|
}
|
|
81897
81900
|
return identifier;
|
|
81898
81901
|
}
|
|
81902
|
+
function readableAlignmentIdentifier2(alignment) {
|
|
81903
|
+
const identifier = alignment.identifier?.trim() ?? "";
|
|
81904
|
+
if (identifier && !isUuidShaped2(identifier)) {
|
|
81905
|
+
return identifier;
|
|
81906
|
+
}
|
|
81907
|
+
const id = alignment.id.trim();
|
|
81908
|
+
return id && !isUuidShaped2(id) ? id : "";
|
|
81909
|
+
}
|
|
81899
81910
|
function canonicalAssessmentStandardRef2(standard) {
|
|
81900
81911
|
const authoredFramework = normalizedWhitespace2(standard.framework);
|
|
81901
81912
|
const framework = canonicalFramework2(authoredFramework, standard.identifier);
|
|
@@ -82002,7 +82013,23 @@ function qtiStandardsFromMetadata(metadata2) {
|
|
|
82002
82013
|
standards.push(...qtiAlignmentStandardsFromMetadata2(metadata2));
|
|
82003
82014
|
return dedupeStandards2(standards);
|
|
82004
82015
|
}
|
|
82005
|
-
|
|
82016
|
+
function qtiAssessmentStandardRefsFromMetadata2(metadata2) {
|
|
82017
|
+
const refs = new Map;
|
|
82018
|
+
for (const alignment of qtiAlignmentStandardsFromMetadata2(metadata2)) {
|
|
82019
|
+
const identifier = readableAlignmentIdentifier2(alignment);
|
|
82020
|
+
if (identifier) {
|
|
82021
|
+
const canonical = canonicalAssessmentStandardRef2({
|
|
82022
|
+
framework: alignment.source,
|
|
82023
|
+
identifier
|
|
82024
|
+
});
|
|
82025
|
+
if (canonical.framework && canonical.identifier) {
|
|
82026
|
+
refs.set(assessmentStandardRefKey2(canonical), canonical);
|
|
82027
|
+
}
|
|
82028
|
+
}
|
|
82029
|
+
}
|
|
82030
|
+
return [...refs.values()];
|
|
82031
|
+
}
|
|
82032
|
+
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;
|
|
82006
82033
|
var init_qti = __esm(() => {
|
|
82007
82034
|
init_timeback4();
|
|
82008
82035
|
init_timeback4();
|
|
@@ -82364,6 +82391,7 @@ var init_qti = __esm(() => {
|
|
|
82364
82391
|
this.name = "QtiScoringArtifactValidationError";
|
|
82365
82392
|
}
|
|
82366
82393
|
};
|
|
82394
|
+
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;
|
|
82367
82395
|
COMMON_CORE_MATH_FRAMEWORK_ALIASES2 = new Set([
|
|
82368
82396
|
"ccssm",
|
|
82369
82397
|
"ccssmath",
|
|
@@ -82573,7 +82601,7 @@ function pruneUnreadableStandards(standards) {
|
|
|
82573
82601
|
return standards.filter(hasReadableStandardLabel);
|
|
82574
82602
|
}
|
|
82575
82603
|
function parseQuestionIndex(title) {
|
|
82576
|
-
const match = title
|
|
82604
|
+
const match = title?.match(/\bquestion[\s_-]*(\d+)\b/i);
|
|
82577
82605
|
const index = match ? Number(match[1]) : Number.NaN;
|
|
82578
82606
|
return Number.isFinite(index) && index > 0 ? index : null;
|
|
82579
82607
|
}
|
|
@@ -82607,14 +82635,15 @@ function qtiQuestionsByIndex(qtiQuestions) {
|
|
|
82607
82635
|
}
|
|
82608
82636
|
return questionsByIndex;
|
|
82609
82637
|
}
|
|
82638
|
+
function normalizedQuestionScore(score, scorePercentile) {
|
|
82639
|
+
const rawValue = numberField(score) ?? numberField(scorePercentile);
|
|
82640
|
+
return rawValue !== null && rawValue > 1 ? rawValue / 100 : rawValue;
|
|
82641
|
+
}
|
|
82610
82642
|
function getQuestionCorrectness(result) {
|
|
82611
82643
|
if (!result) {
|
|
82612
82644
|
return "unknown";
|
|
82613
82645
|
}
|
|
82614
|
-
const
|
|
82615
|
-
const scorePercentile = numberField(result.scorePercentile);
|
|
82616
|
-
const rawValue = score ?? scorePercentile;
|
|
82617
|
-
const value = rawValue !== null && rawValue > 1 ? rawValue / 100 : rawValue;
|
|
82646
|
+
const value = normalizedQuestionScore(result.score, result.scorePercentile);
|
|
82618
82647
|
if (value === null) {
|
|
82619
82648
|
return "unknown";
|
|
82620
82649
|
}
|
|
@@ -82878,10 +82907,71 @@ var init_timeback_mastery_completion_util = __esm(async () => {
|
|
|
82878
82907
|
await init_errors8();
|
|
82879
82908
|
});
|
|
82880
82909
|
|
|
82910
|
+
// ../api-core/src/utils/bounded-map.util.ts
|
|
82911
|
+
function setBounded(map, key, value, maxSize) {
|
|
82912
|
+
map.delete(key);
|
|
82913
|
+
if (map.size >= maxSize) {
|
|
82914
|
+
const oldestKey = map.keys().next().value;
|
|
82915
|
+
if (oldestKey !== undefined) {
|
|
82916
|
+
map.delete(oldestKey);
|
|
82917
|
+
}
|
|
82918
|
+
}
|
|
82919
|
+
map.set(key, value);
|
|
82920
|
+
}
|
|
82921
|
+
|
|
82922
|
+
// ../api-core/src/utils/timeback-qti-test-lookup.util.ts
|
|
82923
|
+
async function resolveQtiTestId(client, testName, parentLineItem) {
|
|
82924
|
+
const cacheKey = `${parentLineItem.sourcedId ?? ""}:${testName}`;
|
|
82925
|
+
const cached = cache.get(cacheKey);
|
|
82926
|
+
if (cached) {
|
|
82927
|
+
setBounded(cache, cacheKey, cached, MAX_CACHE_SIZE);
|
|
82928
|
+
return cached;
|
|
82929
|
+
}
|
|
82930
|
+
let resource = null;
|
|
82931
|
+
const resourceId = sourceIdFromRef(parentLineItem.componentResource);
|
|
82932
|
+
if (resourceId) {
|
|
82933
|
+
resource = await client.api.oneroster.resources.get(resourceId).catch(() => null);
|
|
82934
|
+
}
|
|
82935
|
+
for (const candidate of qtiIdCandidates(parentLineItem, resource)) {
|
|
82936
|
+
const test = await client.qtiApi.assessmentTests.get(candidate).catch(() => null);
|
|
82937
|
+
if (test?.identifier) {
|
|
82938
|
+
setBounded(cache, cacheKey, test.identifier, MAX_CACHE_SIZE);
|
|
82939
|
+
return test.identifier;
|
|
82940
|
+
}
|
|
82941
|
+
}
|
|
82942
|
+
const normalizedTestName = testName.trim();
|
|
82943
|
+
if (!normalizedTestName) {
|
|
82944
|
+
return "";
|
|
82945
|
+
}
|
|
82946
|
+
for (let page = 1;page <= TITLE_LOOKUP_MAX_PAGES; page++) {
|
|
82947
|
+
const list = await client.qtiApi.assessmentTests.list({
|
|
82948
|
+
query: normalizedTestName,
|
|
82949
|
+
limit: TITLE_LOOKUP_LIMIT,
|
|
82950
|
+
page,
|
|
82951
|
+
sort: "title",
|
|
82952
|
+
order: "asc"
|
|
82953
|
+
}).catch(() => null);
|
|
82954
|
+
const qtiTestId = findQtiAssessmentTestIdByTitle(list?.items ?? [], normalizedTestName);
|
|
82955
|
+
if (qtiTestId) {
|
|
82956
|
+
setBounded(cache, cacheKey, qtiTestId, MAX_CACHE_SIZE);
|
|
82957
|
+
return qtiTestId;
|
|
82958
|
+
}
|
|
82959
|
+
const itemCount = list?.items?.length ?? 0;
|
|
82960
|
+
if (!list || itemCount < TITLE_LOOKUP_LIMIT || typeof list.pages === "number" && page >= list.pages) {
|
|
82961
|
+
break;
|
|
82962
|
+
}
|
|
82963
|
+
}
|
|
82964
|
+
return "";
|
|
82965
|
+
}
|
|
82966
|
+
var MAX_CACHE_SIZE = 500, TITLE_LOOKUP_LIMIT = 50, TITLE_LOOKUP_MAX_PAGES = 5, cache;
|
|
82967
|
+
var init_timeback_qti_test_lookup_util = __esm(() => {
|
|
82968
|
+
init_timeback_grade_level_results_util();
|
|
82969
|
+
cache = new Map;
|
|
82970
|
+
});
|
|
82971
|
+
|
|
82881
82972
|
// ../api-core/src/services/timeback-admin.service.ts
|
|
82882
82973
|
class TimebackAdminService {
|
|
82883
82974
|
deps;
|
|
82884
|
-
qtiTestIdCache = new Map;
|
|
82885
82975
|
static XP_PRECISION_FACTOR = 10;
|
|
82886
82976
|
static RECENT_ACTIVITY_LIMIT = 20;
|
|
82887
82977
|
static MAX_STUDENT_ACTIVITY_LIMIT = 200;
|
|
@@ -82896,9 +82986,6 @@ class TimebackAdminService {
|
|
|
82896
82986
|
static GAME_METRICS_FETCH_TIMEOUT_MS = 1e4;
|
|
82897
82987
|
static GAME_METRICS_LIST_FETCH_TIMEOUT_MS = 3000;
|
|
82898
82988
|
static GAME_METRICS_RUN_IDS_PER_REQUEST = 50;
|
|
82899
|
-
static MAX_QTI_TEST_ID_CACHE_SIZE = 500;
|
|
82900
|
-
static QTI_TEST_TITLE_LOOKUP_LIMIT = 50;
|
|
82901
|
-
static QTI_TEST_TITLE_LOOKUP_MAX_PAGES = 5;
|
|
82902
82989
|
constructor(deps) {
|
|
82903
82990
|
this.deps = deps;
|
|
82904
82991
|
}
|
|
@@ -83758,68 +83845,6 @@ class TimebackAdminService {
|
|
|
83758
83845
|
return { activities: activitiesWithGameMetrics, hasMore };
|
|
83759
83846
|
});
|
|
83760
83847
|
}
|
|
83761
|
-
rememberQtiTestId(cacheKey, qtiTestId) {
|
|
83762
|
-
if (!qtiTestId) {
|
|
83763
|
-
this.qtiTestIdCache.delete(cacheKey);
|
|
83764
|
-
return;
|
|
83765
|
-
}
|
|
83766
|
-
if (this.qtiTestIdCache.has(cacheKey)) {
|
|
83767
|
-
this.qtiTestIdCache.delete(cacheKey);
|
|
83768
|
-
} else if (this.qtiTestIdCache.size >= TimebackAdminService.MAX_QTI_TEST_ID_CACHE_SIZE) {
|
|
83769
|
-
const oldestKey = this.qtiTestIdCache.keys().next().value;
|
|
83770
|
-
if (oldestKey !== undefined) {
|
|
83771
|
-
this.qtiTestIdCache.delete(oldestKey);
|
|
83772
|
-
}
|
|
83773
|
-
}
|
|
83774
|
-
this.qtiTestIdCache.set(cacheKey, qtiTestId);
|
|
83775
|
-
}
|
|
83776
|
-
async resolveQtiTestIdForGradeLevelResult(client, testName, parentLineItem) {
|
|
83777
|
-
const cacheKey = `${parentLineItem.sourcedId ?? ""}:${testName}`;
|
|
83778
|
-
const cachedQtiTestId = cacheKey ? this.qtiTestIdCache.get(cacheKey) : undefined;
|
|
83779
|
-
if (cachedQtiTestId) {
|
|
83780
|
-
this.rememberQtiTestId(cacheKey, cachedQtiTestId);
|
|
83781
|
-
return cachedQtiTestId;
|
|
83782
|
-
}
|
|
83783
|
-
let resource = null;
|
|
83784
|
-
const resourceId = sourceIdFromRef(parentLineItem.componentResource);
|
|
83785
|
-
if (resourceId) {
|
|
83786
|
-
resource = await client.api.oneroster.resources.get(resourceId).catch(() => null);
|
|
83787
|
-
}
|
|
83788
|
-
for (const candidate of qtiIdCandidates(parentLineItem, resource)) {
|
|
83789
|
-
const test = await client.qtiApi.assessmentTests.get(candidate).catch(() => null);
|
|
83790
|
-
if (test?.identifier) {
|
|
83791
|
-
if (cacheKey) {
|
|
83792
|
-
this.rememberQtiTestId(cacheKey, test.identifier);
|
|
83793
|
-
}
|
|
83794
|
-
return test.identifier;
|
|
83795
|
-
}
|
|
83796
|
-
}
|
|
83797
|
-
const normalizedTestName = testName.trim();
|
|
83798
|
-
if (!normalizedTestName) {
|
|
83799
|
-
return "";
|
|
83800
|
-
}
|
|
83801
|
-
for (let page = 1;page <= TimebackAdminService.QTI_TEST_TITLE_LOOKUP_MAX_PAGES; page++) {
|
|
83802
|
-
const list = await client.qtiApi.assessmentTests.list({
|
|
83803
|
-
query: normalizedTestName,
|
|
83804
|
-
limit: TimebackAdminService.QTI_TEST_TITLE_LOOKUP_LIMIT,
|
|
83805
|
-
page,
|
|
83806
|
-
sort: "title",
|
|
83807
|
-
order: "asc"
|
|
83808
|
-
}).catch(() => null);
|
|
83809
|
-
const qtiTestId = findQtiAssessmentTestIdByTitle(list?.items ?? [], normalizedTestName);
|
|
83810
|
-
if (qtiTestId) {
|
|
83811
|
-
if (cacheKey) {
|
|
83812
|
-
this.rememberQtiTestId(cacheKey, qtiTestId);
|
|
83813
|
-
}
|
|
83814
|
-
return qtiTestId;
|
|
83815
|
-
}
|
|
83816
|
-
const itemCount = list?.items?.length ?? 0;
|
|
83817
|
-
if (!list || itemCount < TimebackAdminService.QTI_TEST_TITLE_LOOKUP_LIMIT || typeof list.pages === "number" && page >= list.pages) {
|
|
83818
|
-
break;
|
|
83819
|
-
}
|
|
83820
|
-
}
|
|
83821
|
-
return "";
|
|
83822
|
-
}
|
|
83823
83848
|
async getGradeLevelTestResults(user, options) {
|
|
83824
83849
|
return this.withClientTelemetry(async () => {
|
|
83825
83850
|
const { gameId, courseId, studentId } = options;
|
|
@@ -83922,7 +83947,7 @@ class TimebackAdminService {
|
|
|
83922
83947
|
}).then((page) => page.data) : [];
|
|
83923
83948
|
const latestResultsByLineItem = latestAssessmentResultsByLineItem(questionResults);
|
|
83924
83949
|
const testName = resolveGradeLevelTestName(parentResult, parentLineItem);
|
|
83925
|
-
const qtiTestId = await
|
|
83950
|
+
const qtiTestId = await resolveQtiTestId(client, testName, parentLineItem);
|
|
83926
83951
|
const qtiQuestions = qtiTestId ? await client.qtiApi.assessmentTests.getQuestions(qtiTestId).then((response) => response.questions ?? []).catch(() => []) : [];
|
|
83927
83952
|
const questions = mapGradeLevelReviewQuestions({
|
|
83928
83953
|
questionLineItems,
|
|
@@ -84616,6 +84641,7 @@ var init_timeback_admin_service = __esm(async () => {
|
|
|
84616
84641
|
init_timeback_discrepancy_queue_util();
|
|
84617
84642
|
init_timeback_game_metrics_comparison_util();
|
|
84618
84643
|
init_timeback_grade_level_results_util();
|
|
84644
|
+
init_timeback_qti_test_lookup_util();
|
|
84619
84645
|
init_timeback_util();
|
|
84620
84646
|
await __promiseAll([
|
|
84621
84647
|
init_dist5(),
|
|
@@ -92622,6 +92648,128 @@ var init_timeback_create_integration_util = __esm(() => {
|
|
|
92622
92648
|
init_timeback_util();
|
|
92623
92649
|
});
|
|
92624
92650
|
|
|
92651
|
+
// ../api-core/src/utils/timeback-hole-filling-context.util.ts
|
|
92652
|
+
function buildAssignmentResultWhere(options) {
|
|
92653
|
+
return {
|
|
92654
|
+
status: "active",
|
|
92655
|
+
"student.sourcedId": options.studentId,
|
|
92656
|
+
"metadata.resultType": options.resultType,
|
|
92657
|
+
"metadata.assignmentId": options.assignmentId
|
|
92658
|
+
};
|
|
92659
|
+
}
|
|
92660
|
+
function assignmentIdOf(record) {
|
|
92661
|
+
const value = metadataOf(record).assignmentId;
|
|
92662
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
92663
|
+
return String(value);
|
|
92664
|
+
}
|
|
92665
|
+
const text = stringField3(value).trim();
|
|
92666
|
+
return text || undefined;
|
|
92667
|
+
}
|
|
92668
|
+
function toTriggeringResult(result) {
|
|
92669
|
+
const metadata2 = metadataOf(result);
|
|
92670
|
+
const testType = normalizeTestType(metadata2.testType);
|
|
92671
|
+
const assignmentId = assignmentIdOf(result);
|
|
92672
|
+
const lineItemId = sourceIdFromRef(result.assessmentLineItem);
|
|
92673
|
+
if (!assignmentId || !lineItemId || result.scoreStatus !== SCORE_STATUS2.fullyGraded || !isAllowedGradeLevelTestType(testType)) {
|
|
92674
|
+
return null;
|
|
92675
|
+
}
|
|
92676
|
+
return {
|
|
92677
|
+
resultId: result.sourcedId ?? "",
|
|
92678
|
+
assignmentId,
|
|
92679
|
+
lineItemId,
|
|
92680
|
+
testName: stringField3(metadata2.testName) || lineItemId,
|
|
92681
|
+
testType,
|
|
92682
|
+
score: numberField(result.score),
|
|
92683
|
+
scoreDate: result.scoreDate ?? null,
|
|
92684
|
+
totalQuestions: numberField(metadata2.totalQuestions),
|
|
92685
|
+
correctQuestions: numberField(metadata2.correctQuestions)
|
|
92686
|
+
};
|
|
92687
|
+
}
|
|
92688
|
+
function isMissed(resultText, score, scorePercentile) {
|
|
92689
|
+
const marking = stringField3(resultText).trim().toLowerCase();
|
|
92690
|
+
if (marking) {
|
|
92691
|
+
return marking !== "correct";
|
|
92692
|
+
}
|
|
92693
|
+
const value = normalizedQuestionScore(score, scorePercentile);
|
|
92694
|
+
return value !== null && value < 1;
|
|
92695
|
+
}
|
|
92696
|
+
function missedQuestionsFromInlineResponses(result) {
|
|
92697
|
+
const responses = metadataOf(result).responses;
|
|
92698
|
+
const questions = isRecord6(responses) && Array.isArray(responses.questions) ? responses.questions : undefined;
|
|
92699
|
+
if (!questions?.length) {
|
|
92700
|
+
return;
|
|
92701
|
+
}
|
|
92702
|
+
return questions.flatMap((question) => {
|
|
92703
|
+
if (!isRecord6(question) || !isMissed(question.result, question.score)) {
|
|
92704
|
+
return [];
|
|
92705
|
+
}
|
|
92706
|
+
const questionNumber = numberField(question.questionNumber);
|
|
92707
|
+
const qtiItemId = stringField3(question.questionQtiIdentifier).trim();
|
|
92708
|
+
return questionNumber === null ? [] : [{ questionNumber, ...qtiItemId && { qtiItemId } }];
|
|
92709
|
+
});
|
|
92710
|
+
}
|
|
92711
|
+
function questionNumberFromLineItemId(lineItemId) {
|
|
92712
|
+
const match = lineItemId.match(/_q(\d+)$/);
|
|
92713
|
+
const value = match ? Number(match[1]) : Number.NaN;
|
|
92714
|
+
return Number.isInteger(value) && value > 0 ? value : null;
|
|
92715
|
+
}
|
|
92716
|
+
function missedQuestionsFromQuestionResults(questionResults) {
|
|
92717
|
+
return [...latestAssessmentResultsByLineItem(questionResults)].flatMap(([lineItemId, result]) => {
|
|
92718
|
+
const questionNumber = questionNumberFromLineItemId(lineItemId);
|
|
92719
|
+
const responses = metadataOf(result).responses;
|
|
92720
|
+
const response = isRecord6(responses) && isRecord6(responses.response) ? responses.response : undefined;
|
|
92721
|
+
if (questionNumber === null || !isMissed(response?.result, result.score, result.scorePercentile)) {
|
|
92722
|
+
return [];
|
|
92723
|
+
}
|
|
92724
|
+
const qtiItemId = stringField3(response?.questionQtiIdentifier).trim();
|
|
92725
|
+
return [{ questionNumber, ...qtiItemId && { qtiItemId } }];
|
|
92726
|
+
}).toSorted((a, b) => a.questionNumber - b.questionNumber);
|
|
92727
|
+
}
|
|
92728
|
+
function qtiItemIdForQuestionNumber(qtiQuestions) {
|
|
92729
|
+
const byNumber = qtiQuestionsByIndex(qtiQuestions);
|
|
92730
|
+
return (questionNumber) => {
|
|
92731
|
+
const question = byNumber.get(questionNumber) ?? qtiQuestions[questionNumber - 1];
|
|
92732
|
+
return question ? question.question.identifier || question.reference.identifier : undefined;
|
|
92733
|
+
};
|
|
92734
|
+
}
|
|
92735
|
+
function groupMissedStandards(missed, standardsByItemId, itemIdFor) {
|
|
92736
|
+
const byKey = new Map;
|
|
92737
|
+
const untagged = [];
|
|
92738
|
+
for (const question of missed) {
|
|
92739
|
+
const itemId = question.qtiItemId ?? itemIdFor(question.questionNumber);
|
|
92740
|
+
const standards = itemId ? standardsByItemId.get(itemId) ?? [] : [];
|
|
92741
|
+
if (!standards.length) {
|
|
92742
|
+
untagged.push(question.questionNumber);
|
|
92743
|
+
}
|
|
92744
|
+
for (const standard of standards) {
|
|
92745
|
+
const key = `${standard.framework}\x00${standard.identifier}`;
|
|
92746
|
+
const entry = byKey.get(key) ?? {
|
|
92747
|
+
framework: standard.framework,
|
|
92748
|
+
code: standard.identifier,
|
|
92749
|
+
questionNumbers: [],
|
|
92750
|
+
qtiItemIds: []
|
|
92751
|
+
};
|
|
92752
|
+
entry.questionNumbers.push(question.questionNumber);
|
|
92753
|
+
if (itemId && !entry.qtiItemIds.includes(itemId)) {
|
|
92754
|
+
entry.qtiItemIds.push(itemId);
|
|
92755
|
+
}
|
|
92756
|
+
byKey.set(key, entry);
|
|
92757
|
+
}
|
|
92758
|
+
}
|
|
92759
|
+
return {
|
|
92760
|
+
missedStandards: [...byKey.values()].toSorted((a, b) => b.questionNumbers.length - a.questionNumbers.length || a.code.localeCompare(b.code, undefined, { numeric: true })),
|
|
92761
|
+
untaggedQuestionNumbers: untagged.toSorted((a, b) => a - b)
|
|
92762
|
+
};
|
|
92763
|
+
}
|
|
92764
|
+
function standardsFromQtiItem(item) {
|
|
92765
|
+
return qtiAssessmentStandardRefsFromMetadata2(metadataOf(item));
|
|
92766
|
+
}
|
|
92767
|
+
var init_timeback_hole_filling_context_util = __esm(() => {
|
|
92768
|
+
init_constants2();
|
|
92769
|
+
init_qti();
|
|
92770
|
+
init_timeback_grade_level_results_util();
|
|
92771
|
+
});
|
|
92772
|
+
|
|
92625
92773
|
// ../api-core/src/utils/timeback-promotion.util.ts
|
|
92626
92774
|
async function promoteCompletedCourse({
|
|
92627
92775
|
db,
|
|
@@ -92751,14 +92899,20 @@ var init_timeback_service = __esm(async () => {
|
|
|
92751
92899
|
init_helpers_index();
|
|
92752
92900
|
init_tables_index();
|
|
92753
92901
|
init_spans();
|
|
92902
|
+
init_constants2();
|
|
92754
92903
|
init_types2();
|
|
92755
92904
|
init_errors2();
|
|
92756
92905
|
init_timeback_create_integration_util();
|
|
92906
|
+
init_timeback_grade_level_results_util();
|
|
92907
|
+
init_timeback_hole_filling_context_util();
|
|
92757
92908
|
init_timeback_promotion_util();
|
|
92909
|
+
init_timeback_qti_test_lookup_util();
|
|
92758
92910
|
init_timeback_removed_integration_util();
|
|
92759
92911
|
init_timeback_util();
|
|
92760
92912
|
await init_dist5();
|
|
92761
92913
|
TimebackService = class TimebackService {
|
|
92914
|
+
static HOLE_FILLING_CONTEXT_TTL_MS = 10 * 60 * 1000;
|
|
92915
|
+
static HOLE_FILLING_CONTEXT_CACHE_SIZE = 1000;
|
|
92762
92916
|
static HEARTBEAT_DEDUPE_TTL_MS = 5 * 60 * 1000;
|
|
92763
92917
|
static processedHeartbeatWindows = new Map;
|
|
92764
92918
|
static inFlightHeartbeatWindows = new Map;
|
|
@@ -92766,6 +92920,7 @@ var init_timeback_service = __esm(async () => {
|
|
|
92766
92920
|
static COMPLETION_IN_FLIGHT_SETTLE_DELAY_MS = 400;
|
|
92767
92921
|
static COMPLETION_IN_FLIGHT_SETTLE_ATTEMPTS = 5;
|
|
92768
92922
|
deps;
|
|
92923
|
+
holeFillingContextCache = new Map;
|
|
92769
92924
|
static cleanHeartbeatDedupeCache(now = Date.now()) {
|
|
92770
92925
|
for (const [key, timestamp] of this.processedHeartbeatWindows) {
|
|
92771
92926
|
if (now - timestamp > this.HEARTBEAT_DEDUPE_TTL_MS) {
|
|
@@ -93139,19 +93294,163 @@ var init_timeback_service = __esm(async () => {
|
|
|
93139
93294
|
}
|
|
93140
93295
|
async getUserEnrollments(userId, gameId) {
|
|
93141
93296
|
return this.withClientTelemetry(async () => {
|
|
93142
|
-
const
|
|
93143
|
-
|
|
93144
|
-
|
|
93145
|
-
|
|
93146
|
-
|
|
93147
|
-
|
|
93297
|
+
const enrollments = await this.fetchEnrollments(await this.requireTimebackId(userId));
|
|
93298
|
+
return gameId ? filterEnrollmentsByGame(enrollments, gameId) : enrollments;
|
|
93299
|
+
});
|
|
93300
|
+
}
|
|
93301
|
+
async requireTimebackId(userId) {
|
|
93302
|
+
const userData = await this.deps.db.query.users.findFirst({
|
|
93303
|
+
where: eq(users.id, userId),
|
|
93304
|
+
columns: { id: true, timebackId: true }
|
|
93305
|
+
});
|
|
93306
|
+
if (!userData) {
|
|
93307
|
+
throw new NotFoundError("User", userId);
|
|
93308
|
+
}
|
|
93309
|
+
if (!userData.timebackId) {
|
|
93310
|
+
throw new NotFoundError("Timeback account not found for user");
|
|
93311
|
+
}
|
|
93312
|
+
return userData.timebackId;
|
|
93313
|
+
}
|
|
93314
|
+
async getHoleFillingContext(userId, gameId) {
|
|
93315
|
+
return this.withClientTelemetry(async () => {
|
|
93316
|
+
setAttribute("app.timeback.operation", "get_hole_filling_context");
|
|
93317
|
+
const studentId = await this.requireTimebackId(userId);
|
|
93318
|
+
const enrollments = filterEnrollmentsByGame(await this.fetchEnrollments(studentId), gameId);
|
|
93319
|
+
const holeFilling = enrollments.filter((enrollment) => enrollment.kind === HOLE_FILLING_GAME_TIMEBACK_INTEGRATION_KIND);
|
|
93320
|
+
setAttribute("app.timeback.hole_filling_enrollment_count", holeFilling.length);
|
|
93321
|
+
if (holeFilling.length === 0) {
|
|
93322
|
+
return { contexts: [] };
|
|
93148
93323
|
}
|
|
93149
|
-
|
|
93150
|
-
|
|
93324
|
+
const contexts = await Promise.all(holeFilling.map((enrollment) => this.buildHoleFillingContext(studentId, enrollment)));
|
|
93325
|
+
return { contexts };
|
|
93326
|
+
});
|
|
93327
|
+
}
|
|
93328
|
+
async buildHoleFillingContext(studentId, enrollment) {
|
|
93329
|
+
const client = this.requireClient();
|
|
93330
|
+
const grade = enrollment.grade;
|
|
93331
|
+
const subject = enrollment.subject;
|
|
93332
|
+
const enrollmentContext = {
|
|
93333
|
+
courseId: enrollment.courseId,
|
|
93334
|
+
baseCourseId: enrollment.baseCourseId ?? "",
|
|
93335
|
+
...enrollment.id && { enrollmentId: enrollment.id },
|
|
93336
|
+
grade,
|
|
93337
|
+
subject,
|
|
93338
|
+
beginDate: enrollment.beginDate ?? null
|
|
93339
|
+
};
|
|
93340
|
+
const stampedAssignmentId = assignmentIdOf({ metadata: enrollment.enrollmentMetadata });
|
|
93341
|
+
let parent = null;
|
|
93342
|
+
if (stampedAssignmentId) {
|
|
93343
|
+
const where = {
|
|
93344
|
+
...buildAssignmentResultWhere({
|
|
93345
|
+
studentId,
|
|
93346
|
+
assignmentId: stampedAssignmentId,
|
|
93347
|
+
resultType: "assessment"
|
|
93348
|
+
}),
|
|
93349
|
+
scoreStatus: SCORE_STATUS2.fullyGraded
|
|
93350
|
+
};
|
|
93351
|
+
const page = await client.api.oneroster.assessmentResults.list({
|
|
93352
|
+
limit: 1,
|
|
93353
|
+
where,
|
|
93354
|
+
sort: "scoreDate",
|
|
93355
|
+
orderBy: "desc"
|
|
93356
|
+
});
|
|
93357
|
+
parent = page.data[0] ?? null;
|
|
93358
|
+
}
|
|
93359
|
+
const summary = parent ? toTriggeringResult(parent) : null;
|
|
93360
|
+
let trigger = "not_stamped";
|
|
93361
|
+
if (stampedAssignmentId) {
|
|
93362
|
+
trigger = summary ? "found" : "not_found";
|
|
93363
|
+
}
|
|
93364
|
+
setAttributes({
|
|
93365
|
+
"app.timeback.hole_filling_trigger": trigger,
|
|
93366
|
+
"app.timeback.hole_filling_assignment_id": stampedAssignmentId
|
|
93367
|
+
});
|
|
93368
|
+
if (!parent || !summary) {
|
|
93369
|
+
return {
|
|
93370
|
+
enrollment: enrollmentContext,
|
|
93371
|
+
triggeringResult: null,
|
|
93372
|
+
missedStandards: [],
|
|
93373
|
+
untaggedMissedQuestionNumbers: []
|
|
93374
|
+
};
|
|
93375
|
+
}
|
|
93376
|
+
const cacheKey = `${enrollment.courseId}:${studentId}:${summary.resultId}`;
|
|
93377
|
+
const cached = this.holeFillingContextCache.get(cacheKey);
|
|
93378
|
+
if (cached && Date.now() - cached.at < TimebackService.HOLE_FILLING_CONTEXT_TTL_MS) {
|
|
93379
|
+
return { ...cached.context, enrollment: enrollmentContext };
|
|
93380
|
+
}
|
|
93381
|
+
const { context, complete } = await this.resolveMissedStandards(studentId, parent, summary, enrollmentContext);
|
|
93382
|
+
setAttribute("app.timeback.hole_filling_standards_complete", complete);
|
|
93383
|
+
if (complete) {
|
|
93384
|
+
setBounded(this.holeFillingContextCache, cacheKey, { at: Date.now(), context }, TimebackService.HOLE_FILLING_CONTEXT_CACHE_SIZE);
|
|
93385
|
+
}
|
|
93386
|
+
return context;
|
|
93387
|
+
}
|
|
93388
|
+
async resolveMissedStandards(studentId, parent, summary, enrollmentContext) {
|
|
93389
|
+
const client = this.requireClient();
|
|
93390
|
+
let complete = true;
|
|
93391
|
+
let missed = missedQuestionsFromInlineResponses(parent);
|
|
93392
|
+
if (!missed) {
|
|
93393
|
+
const where = buildAssignmentResultWhere({
|
|
93394
|
+
studentId,
|
|
93395
|
+
assignmentId: summary.assignmentId,
|
|
93396
|
+
resultType: "question"
|
|
93397
|
+
});
|
|
93398
|
+
const questionResults = await client.api.oneroster.assessmentResults.listAll({
|
|
93399
|
+
limit: 200,
|
|
93400
|
+
where,
|
|
93401
|
+
sort: "scoreDate",
|
|
93402
|
+
orderBy: "desc"
|
|
93403
|
+
});
|
|
93404
|
+
missed = missedQuestionsFromQuestionResults(questionResults);
|
|
93405
|
+
}
|
|
93406
|
+
let qtiTestId = "";
|
|
93407
|
+
let qtiQuestions = [];
|
|
93408
|
+
const standardsByItemId = new Map;
|
|
93409
|
+
if (missed.length > 0 && !client.hasQti) {
|
|
93410
|
+
complete = false;
|
|
93411
|
+
missed = missed.map(({ questionNumber }) => ({ questionNumber }));
|
|
93412
|
+
} else if (missed.some((question) => !question.qtiItemId)) {
|
|
93413
|
+
const lineItem = await client.api.oneroster.assessmentLineItems.get(summary.lineItemId).catch(() => null);
|
|
93414
|
+
qtiTestId = lineItem ? await resolveQtiTestId(client, resolveGradeLevelTestName(parent, lineItem), lineItem) : "";
|
|
93415
|
+
complete &&= Boolean(qtiTestId);
|
|
93416
|
+
if (qtiTestId) {
|
|
93417
|
+
qtiQuestions = await client.qtiApi.assessmentTests.getQuestions(qtiTestId).then((response) => response.questions ?? []).catch(() => {
|
|
93418
|
+
complete = false;
|
|
93419
|
+
return [];
|
|
93420
|
+
});
|
|
93421
|
+
for (const question of qtiQuestions) {
|
|
93422
|
+
const id = question.question.identifier || question.reference.identifier;
|
|
93423
|
+
if (id) {
|
|
93424
|
+
standardsByItemId.set(id, standardsFromQtiItem(question.question));
|
|
93425
|
+
}
|
|
93426
|
+
}
|
|
93151
93427
|
}
|
|
93152
|
-
|
|
93153
|
-
|
|
93428
|
+
}
|
|
93429
|
+
const itemIdsToFetch = [
|
|
93430
|
+
...new Set(missed.flatMap((question) => question.qtiItemId && !standardsByItemId.has(question.qtiItemId) ? [question.qtiItemId] : []))
|
|
93431
|
+
];
|
|
93432
|
+
const items = await Promise.all(itemIdsToFetch.map((id) => client.qtiApi.assessmentItems.get(id).then((item) => [id, standardsFromQtiItem(item)]).catch(() => {
|
|
93433
|
+
complete = false;
|
|
93434
|
+
return [id, []];
|
|
93435
|
+
})));
|
|
93436
|
+
for (const [id, standards] of items) {
|
|
93437
|
+
standardsByItemId.set(id, standards);
|
|
93438
|
+
}
|
|
93439
|
+
const { missedStandards, untaggedQuestionNumbers } = groupMissedStandards(missed, standardsByItemId, qtiItemIdForQuestionNumber(qtiQuestions));
|
|
93440
|
+
setAttributes({
|
|
93441
|
+
"app.timeback.hole_filling_missed_question_count": missed.length,
|
|
93442
|
+
"app.timeback.hole_filling_missed_standard_count": missedStandards.length,
|
|
93443
|
+
"app.timeback.hole_filling_untagged_question_count": untaggedQuestionNumbers.length
|
|
93154
93444
|
});
|
|
93445
|
+
return {
|
|
93446
|
+
context: {
|
|
93447
|
+
enrollment: enrollmentContext,
|
|
93448
|
+
triggeringResult: { ...summary, ...qtiTestId && { qtiTestId } },
|
|
93449
|
+
missedStandards,
|
|
93450
|
+
untaggedMissedQuestionNumbers: untaggedQuestionNumbers
|
|
93451
|
+
},
|
|
93452
|
+
complete
|
|
93453
|
+
};
|
|
93155
93454
|
}
|
|
93156
93455
|
async getUserDataByTimebackId(timebackId) {
|
|
93157
93456
|
return this.withClientTelemetry(async () => {
|
|
@@ -96711,33 +97010,33 @@ var init_auth_provider = __esm(async () => {
|
|
|
96711
97010
|
function createSandboxCacheProvider() {
|
|
96712
97011
|
return {
|
|
96713
97012
|
async get(key) {
|
|
96714
|
-
const entry =
|
|
97013
|
+
const entry = cache2.get(key);
|
|
96715
97014
|
if (!entry) {
|
|
96716
97015
|
return null;
|
|
96717
97016
|
}
|
|
96718
97017
|
if (entry.expiresAt < Date.now()) {
|
|
96719
|
-
|
|
97018
|
+
cache2.delete(key);
|
|
96720
97019
|
return null;
|
|
96721
97020
|
}
|
|
96722
97021
|
return entry.value;
|
|
96723
97022
|
},
|
|
96724
97023
|
async set(key, value, ttlSeconds = 300) {
|
|
96725
|
-
|
|
97024
|
+
cache2.set(key, {
|
|
96726
97025
|
value,
|
|
96727
97026
|
expiresAt: Date.now() + ttlSeconds * 1000
|
|
96728
97027
|
});
|
|
96729
97028
|
},
|
|
96730
97029
|
async delete(key) {
|
|
96731
|
-
|
|
97030
|
+
cache2.delete(key);
|
|
96732
97031
|
}
|
|
96733
97032
|
};
|
|
96734
97033
|
}
|
|
96735
97034
|
function clearSandboxCache() {
|
|
96736
|
-
|
|
97035
|
+
cache2.clear();
|
|
96737
97036
|
}
|
|
96738
|
-
var
|
|
97037
|
+
var cache2;
|
|
96739
97038
|
var init_cache_provider = __esm(() => {
|
|
96740
|
-
|
|
97039
|
+
cache2 = new Map;
|
|
96741
97040
|
});
|
|
96742
97041
|
|
|
96743
97042
|
// ../utils/src/keys.ts
|
|
@@ -152965,7 +153264,7 @@ function parseQtiLibraryParams(searchParams) {
|
|
|
152965
153264
|
limit
|
|
152966
153265
|
};
|
|
152967
153266
|
}
|
|
152968
|
-
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;
|
|
153267
|
+
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;
|
|
152969
153268
|
var init_timeback_controller = __esm(() => {
|
|
152970
153269
|
init_esm();
|
|
152971
153270
|
init_src();
|
|
@@ -152991,6 +153290,12 @@ var init_timeback_controller = __esm(() => {
|
|
|
152991
153290
|
});
|
|
152992
153291
|
getUser = requireNonAnonymous(async (ctx) => ctx.services.timeback.getUserData(ctx.user.id, ctx.gameId));
|
|
152993
153292
|
getUserEnrollments = requireNonAnonymous(async (ctx) => ctx.services.timeback.getUserEnrollments(ctx.user.id, ctx.gameId));
|
|
153293
|
+
getUserHoleFillingContext = requireNonAnonymous(async (ctx) => {
|
|
153294
|
+
if (!ctx.gameId) {
|
|
153295
|
+
throw ApiError.forbidden("Hole-filling context requires a game token");
|
|
153296
|
+
}
|
|
153297
|
+
return ctx.services.timeback.getHoleFillingContext(ctx.user.id, ctx.gameId);
|
|
153298
|
+
});
|
|
152994
153299
|
getUserById = requireNonAnonymous(async (ctx) => {
|
|
152995
153300
|
const timebackId = ctx.params.timebackId;
|
|
152996
153301
|
if (!timebackId) {
|
|
@@ -153844,6 +154149,7 @@ var init_timeback_controller = __esm(() => {
|
|
|
153844
154149
|
populateStudent,
|
|
153845
154150
|
getUser,
|
|
153846
154151
|
getUserEnrollments,
|
|
154152
|
+
getUserHoleFillingContext,
|
|
153847
154153
|
getUserById,
|
|
153848
154154
|
setupIntegration,
|
|
153849
154155
|
getIntegrations,
|
|
@@ -154906,6 +155212,12 @@ var init_timeback8 = __esm(async () => {
|
|
|
154906
155212
|
}
|
|
154907
155213
|
return handle2(timeback2.getUserEnrollments)(c);
|
|
154908
155214
|
});
|
|
155215
|
+
timebackRouter.get("/user/hole-filling-context", async (c) => {
|
|
155216
|
+
if (shouldMockTimeback()) {
|
|
155217
|
+
return c.json({ contexts: [] });
|
|
155218
|
+
}
|
|
155219
|
+
return handle2(timeback2.getUserHoleFillingContext)(c);
|
|
155220
|
+
});
|
|
154909
155221
|
timebackRouter.get("/user/:timebackId", async (c) => {
|
|
154910
155222
|
const user = c.get("user");
|
|
154911
155223
|
if (!user) {
|