@playcademy/vite-plugin 0.1.34 → 0.1.36
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 +192 -127
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -41199,7 +41199,7 @@ var import_picocolors7 = __toESM(require_picocolors(), 1);
|
|
|
41199
41199
|
// package.json
|
|
41200
41200
|
var package_default = {
|
|
41201
41201
|
name: "@playcademy/vite-plugin",
|
|
41202
|
-
version: "0.1.
|
|
41202
|
+
version: "0.1.35",
|
|
41203
41203
|
type: "module",
|
|
41204
41204
|
exports: {
|
|
41205
41205
|
".": {
|
|
@@ -190550,14 +190550,30 @@ function createOneRosterNamespace(client2) {
|
|
|
190550
190550
|
update: async (sourcedId, data) => {
|
|
190551
190551
|
return client2["request"](`${ONEROSTER_ENDPOINTS.assessmentResults}/${sourcedId}`, "PUT", data);
|
|
190552
190552
|
},
|
|
190553
|
-
|
|
190553
|
+
getAttemptStats: async (studentId, lineItemId) => {
|
|
190554
190554
|
try {
|
|
190555
190555
|
const filter2 = `student.sourcedId='${studentId}' AND assessmentLineItem.sourcedId='${lineItemId}'`;
|
|
190556
|
-
const url = `${ONEROSTER_ENDPOINTS.assessmentResults}?filter=${encodeURIComponent(filter2)}
|
|
190556
|
+
const url = `${ONEROSTER_ENDPOINTS.assessmentResults}?filter=${encodeURIComponent(filter2)}`;
|
|
190557
190557
|
const response = await client2["request"](url, "GET");
|
|
190558
|
-
|
|
190558
|
+
const results = response.assessmentResults || [];
|
|
190559
|
+
if (results.length === 0)
|
|
190560
|
+
return null;
|
|
190561
|
+
let maxAttemptResult = results[0];
|
|
190562
|
+
let maxAttemptNumber = maxAttemptResult.metadata?.attemptNumber || 0;
|
|
190563
|
+
let activeAttemptCount = 0;
|
|
190564
|
+
for (const result of results) {
|
|
190565
|
+
const attemptNumber = result.metadata?.attemptNumber || 0;
|
|
190566
|
+
if (attemptNumber > maxAttemptNumber) {
|
|
190567
|
+
maxAttemptNumber = attemptNumber;
|
|
190568
|
+
maxAttemptResult = result;
|
|
190569
|
+
}
|
|
190570
|
+
if (result.status === "active") {
|
|
190571
|
+
activeAttemptCount++;
|
|
190572
|
+
}
|
|
190573
|
+
}
|
|
190574
|
+
return { maxAttemptNumber, activeAttemptCount, maxAttemptResult };
|
|
190559
190575
|
} catch (error2) {
|
|
190560
|
-
logTimebackError("query
|
|
190576
|
+
logTimebackError("query attempt stats", error2, {
|
|
190561
190577
|
studentId,
|
|
190562
190578
|
lineItemId
|
|
190563
190579
|
});
|
|
@@ -190895,9 +190911,6 @@ class TimebackCacheManager {
|
|
|
190895
190911
|
log32.debug("[TimebackCacheManager] Cache cleanup completed");
|
|
190896
190912
|
}
|
|
190897
190913
|
}
|
|
190898
|
-
function kebabToTitleCase(kebabStr) {
|
|
190899
|
-
return kebabStr.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
190900
|
-
}
|
|
190901
190914
|
init_constants();
|
|
190902
190915
|
init_constants();
|
|
190903
190916
|
var isObject2 = (value) => typeof value === "object" && value !== null;
|
|
@@ -190910,6 +190923,152 @@ function isPlaycademyResourceMetadata(value) {
|
|
|
190910
190923
|
}
|
|
190911
190924
|
return isObject2(value.mastery);
|
|
190912
190925
|
}
|
|
190926
|
+
|
|
190927
|
+
class MasteryTracker {
|
|
190928
|
+
cacheManager;
|
|
190929
|
+
onerosterNamespace;
|
|
190930
|
+
edubridgeNamespace;
|
|
190931
|
+
constructor(cacheManager, onerosterNamespace, edubridgeNamespace) {
|
|
190932
|
+
this.cacheManager = cacheManager;
|
|
190933
|
+
this.onerosterNamespace = onerosterNamespace;
|
|
190934
|
+
this.edubridgeNamespace = edubridgeNamespace;
|
|
190935
|
+
}
|
|
190936
|
+
async checkProgress(input) {
|
|
190937
|
+
const { studentId, courseId, resourceId, masteredUnits } = input;
|
|
190938
|
+
if (typeof masteredUnits !== "number" || masteredUnits <= 0) {
|
|
190939
|
+
return;
|
|
190940
|
+
}
|
|
190941
|
+
const masterableUnits = await this.resolveMasterableUnits(resourceId);
|
|
190942
|
+
if (!masterableUnits || masterableUnits <= 0) {
|
|
190943
|
+
log32.warn("[MasteryTracker] No masterableUnits configured for course", {
|
|
190944
|
+
courseId,
|
|
190945
|
+
resourceId
|
|
190946
|
+
});
|
|
190947
|
+
return;
|
|
190948
|
+
}
|
|
190949
|
+
const facts = await this.fetchEnrollmentAnalyticsFacts(studentId, courseId);
|
|
190950
|
+
if (!facts) {
|
|
190951
|
+
log32.warn("[MasteryTracker] Unable to retrieve analytics for mastery-based completion", {
|
|
190952
|
+
studentId,
|
|
190953
|
+
courseId
|
|
190954
|
+
});
|
|
190955
|
+
return;
|
|
190956
|
+
}
|
|
190957
|
+
const historicalMasteredUnits = this.sumAnalyticsMetric(facts, "masteredUnits");
|
|
190958
|
+
const totalMastered = historicalMasteredUnits + masteredUnits;
|
|
190959
|
+
const rawPct = totalMastered / masterableUnits * 100;
|
|
190960
|
+
const pctCompleteApp = Math.min(100, Math.max(0, Math.round(rawPct)));
|
|
190961
|
+
const masteryAchieved = totalMastered >= masterableUnits;
|
|
190962
|
+
return { pctCompleteApp, masteryAchieved };
|
|
190963
|
+
}
|
|
190964
|
+
async createCompletionEntry(studentId, courseId, classId, appName) {
|
|
190965
|
+
const ids = deriveSourcedIds(courseId);
|
|
190966
|
+
const lineItemId = `${ids.course}-mastery-completion-assessment`;
|
|
190967
|
+
const resultId = `${lineItemId}:${studentId}:completion`;
|
|
190968
|
+
try {
|
|
190969
|
+
await this.onerosterNamespace.assessmentLineItems.findOrCreate(lineItemId, {
|
|
190970
|
+
sourcedId: lineItemId,
|
|
190971
|
+
title: "Mastery Completion",
|
|
190972
|
+
status: ONEROSTER_STATUS.active,
|
|
190973
|
+
...classId ? { class: { sourcedId: classId } } : { course: { sourcedId: ids.course } },
|
|
190974
|
+
...ids.componentResource ? { componentResource: { sourcedId: ids.componentResource } } : {}
|
|
190975
|
+
});
|
|
190976
|
+
await this.onerosterNamespace.assessmentResults.upsert(resultId, {
|
|
190977
|
+
sourcedId: resultId,
|
|
190978
|
+
status: ONEROSTER_STATUS.active,
|
|
190979
|
+
assessmentLineItem: { sourcedId: lineItemId },
|
|
190980
|
+
student: { sourcedId: studentId },
|
|
190981
|
+
score: 100,
|
|
190982
|
+
scoreDate: new Date().toISOString(),
|
|
190983
|
+
scoreStatus: SCORE_STATUS.fullyGraded,
|
|
190984
|
+
inProgress: "false",
|
|
190985
|
+
metadata: {
|
|
190986
|
+
isMasteryCompletion: true,
|
|
190987
|
+
completedAt: new Date().toISOString(),
|
|
190988
|
+
appName
|
|
190989
|
+
}
|
|
190990
|
+
});
|
|
190991
|
+
log32.info("[MasteryTracker] Created mastery completion entry", {
|
|
190992
|
+
studentId,
|
|
190993
|
+
lineItemId,
|
|
190994
|
+
resultId
|
|
190995
|
+
});
|
|
190996
|
+
} catch (error2) {
|
|
190997
|
+
log32.error("[MasteryTracker] Failed to create mastery completion entry", {
|
|
190998
|
+
studentId,
|
|
190999
|
+
lineItemId,
|
|
191000
|
+
error: error2
|
|
191001
|
+
});
|
|
191002
|
+
}
|
|
191003
|
+
}
|
|
191004
|
+
async resolveMasterableUnits(resourceId) {
|
|
191005
|
+
if (!resourceId) {
|
|
191006
|
+
return;
|
|
191007
|
+
}
|
|
191008
|
+
const cached = this.cacheManager.getResourceMasterableUnits(resourceId);
|
|
191009
|
+
if (cached !== undefined) {
|
|
191010
|
+
return cached === null ? undefined : cached;
|
|
191011
|
+
}
|
|
191012
|
+
try {
|
|
191013
|
+
const resource = await this.onerosterNamespace.resources.get(resourceId);
|
|
191014
|
+
const playcademyMetadata = resource.metadata?.playcademy;
|
|
191015
|
+
if (!playcademyMetadata) {
|
|
191016
|
+
return;
|
|
191017
|
+
}
|
|
191018
|
+
const masterableUnits = isPlaycademyResourceMetadata(playcademyMetadata) ? playcademyMetadata.mastery?.masterableUnits : undefined;
|
|
191019
|
+
this.cacheManager.setResourceMasterableUnits(resourceId, masterableUnits ?? null);
|
|
191020
|
+
return masterableUnits;
|
|
191021
|
+
} catch (error2) {
|
|
191022
|
+
log32.error("[MasteryTracker] Failed to fetch resource metadata for mastery config", {
|
|
191023
|
+
resourceId,
|
|
191024
|
+
error: error2
|
|
191025
|
+
});
|
|
191026
|
+
this.cacheManager.setResourceMasterableUnits(resourceId, null);
|
|
191027
|
+
return;
|
|
191028
|
+
}
|
|
191029
|
+
}
|
|
191030
|
+
async fetchEnrollmentAnalyticsFacts(studentId, courseId) {
|
|
191031
|
+
try {
|
|
191032
|
+
const enrollments = await this.edubridgeNamespace.enrollments.listByUser(studentId);
|
|
191033
|
+
const enrollment = enrollments.find((e2) => e2.course.id === courseId);
|
|
191034
|
+
if (!enrollment) {
|
|
191035
|
+
log32.warn("[MasteryTracker] Enrollment not found for student/course", {
|
|
191036
|
+
studentId,
|
|
191037
|
+
courseId
|
|
191038
|
+
});
|
|
191039
|
+
return;
|
|
191040
|
+
}
|
|
191041
|
+
const analytics = await this.edubridgeNamespace.analytics.getEnrollmentFacts(enrollment.id);
|
|
191042
|
+
return analytics.facts;
|
|
191043
|
+
} catch (error2) {
|
|
191044
|
+
log32.error("[MasteryTracker] Failed to load enrollment analytics facts", {
|
|
191045
|
+
studentId,
|
|
191046
|
+
courseId,
|
|
191047
|
+
error: error2
|
|
191048
|
+
});
|
|
191049
|
+
return;
|
|
191050
|
+
}
|
|
191051
|
+
}
|
|
191052
|
+
sumAnalyticsMetric(facts, metric) {
|
|
191053
|
+
if (!facts) {
|
|
191054
|
+
return 0;
|
|
191055
|
+
}
|
|
191056
|
+
let total = 0;
|
|
191057
|
+
Object.values(facts).forEach((dateFacts) => {
|
|
191058
|
+
Object.values(dateFacts).forEach((subjectFacts) => {
|
|
191059
|
+
const metrics = subjectFacts.activityMetrics;
|
|
191060
|
+
if (metrics && typeof metrics[metric] === "number") {
|
|
191061
|
+
total += metrics[metric];
|
|
191062
|
+
}
|
|
191063
|
+
});
|
|
191064
|
+
});
|
|
191065
|
+
return total;
|
|
191066
|
+
}
|
|
191067
|
+
}
|
|
191068
|
+
function kebabToTitleCase(kebabStr) {
|
|
191069
|
+
return kebabStr.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
191070
|
+
}
|
|
191071
|
+
init_constants();
|
|
190913
191072
|
function validateProgressData(progressData) {
|
|
190914
191073
|
if (!progressData.subject) {
|
|
190915
191074
|
throw new ConfigurationError("subject", "Subject is required for Caliper events. Provide it in progressData.subject");
|
|
@@ -190963,13 +191122,13 @@ class ProgressRecorder {
|
|
|
190963
191122
|
cacheManager;
|
|
190964
191123
|
onerosterNamespace;
|
|
190965
191124
|
caliperNamespace;
|
|
190966
|
-
|
|
190967
|
-
constructor(studentResolver, cacheManager, onerosterNamespace, caliperNamespace,
|
|
191125
|
+
masteryTracker;
|
|
191126
|
+
constructor(studentResolver, cacheManager, onerosterNamespace, caliperNamespace, masteryTracker) {
|
|
190968
191127
|
this.studentResolver = studentResolver;
|
|
190969
191128
|
this.cacheManager = cacheManager;
|
|
190970
191129
|
this.onerosterNamespace = onerosterNamespace;
|
|
190971
191130
|
this.caliperNamespace = caliperNamespace;
|
|
190972
|
-
this.
|
|
191131
|
+
this.masteryTracker = masteryTracker;
|
|
190973
191132
|
}
|
|
190974
191133
|
async record(courseId, studentIdentifier, progressData) {
|
|
190975
191134
|
validateProgressData(progressData);
|
|
@@ -190978,22 +191137,22 @@ class ProgressRecorder {
|
|
|
190978
191137
|
const { score, totalQuestions, correctQuestions, xpEarned, masteredUnits, attemptNumber } = progressData;
|
|
190979
191138
|
const actualLineItemId = await this.resolveAssessmentLineItem(activityId, activityName, progressData.classId, ids);
|
|
190980
191139
|
const currentAttemptNumber = await this.resolveAttemptNumber(attemptNumber, score, studentId, actualLineItemId);
|
|
190981
|
-
const
|
|
190982
|
-
const calculatedXp = this.calculateXpForProgress(progressData, totalQuestions, correctQuestions, xpEarned,
|
|
191140
|
+
const isFirstActiveAttempt = currentAttemptNumber === 1;
|
|
191141
|
+
const calculatedXp = this.calculateXpForProgress(progressData, totalQuestions, correctQuestions, xpEarned, isFirstActiveAttempt);
|
|
190983
191142
|
let extensions = progressData.extensions;
|
|
190984
|
-
const
|
|
191143
|
+
const masteryProgress = await this.masteryTracker.checkProgress({
|
|
190985
191144
|
studentId,
|
|
190986
191145
|
courseId,
|
|
190987
|
-
|
|
190988
|
-
|
|
191146
|
+
resourceId: ids.resource,
|
|
191147
|
+
masteredUnits: progressData.masteredUnits ?? 0
|
|
190989
191148
|
});
|
|
190990
191149
|
let pctCompleteApp;
|
|
190991
191150
|
let masteryAchieved = false;
|
|
190992
|
-
let scoreStatus = SCORE_STATUS.
|
|
191151
|
+
let scoreStatus = SCORE_STATUS.fullyGraded;
|
|
190993
191152
|
const inProgress = "false";
|
|
190994
|
-
if (
|
|
190995
|
-
masteryAchieved =
|
|
190996
|
-
pctCompleteApp =
|
|
191153
|
+
if (masteryProgress) {
|
|
191154
|
+
masteryAchieved = masteryProgress.masteryAchieved;
|
|
191155
|
+
pctCompleteApp = masteryProgress.pctCompleteApp;
|
|
190997
191156
|
extensions = {
|
|
190998
191157
|
...extensions || {},
|
|
190999
191158
|
...pctCompleteApp !== undefined ? { pctCompleteApp } : {}
|
|
@@ -191011,6 +191170,9 @@ class ProgressRecorder {
|
|
|
191011
191170
|
attemptNumber: currentAttemptNumber
|
|
191012
191171
|
});
|
|
191013
191172
|
}
|
|
191173
|
+
if (masteryAchieved) {
|
|
191174
|
+
await this.masteryTracker.createCompletionEntry(studentId, courseId, progressData.classId, progressData.appName);
|
|
191175
|
+
}
|
|
191014
191176
|
await this.emitCaliperEvent(studentId, studentEmail, activityId, activityName, ids.course, courseName, totalQuestions, correctQuestions, calculatedXp, masteredUnits, currentAttemptNumber, progressData, extensions);
|
|
191015
191177
|
return {
|
|
191016
191178
|
xpAwarded: calculatedXp,
|
|
@@ -191039,8 +191201,9 @@ class ProgressRecorder {
|
|
|
191039
191201
|
return actualLineItemId;
|
|
191040
191202
|
}
|
|
191041
191203
|
async resolveAttemptNumber(providedAttemptNumber, score, studentId, lineItemId) {
|
|
191042
|
-
if (providedAttemptNumber)
|
|
191204
|
+
if (providedAttemptNumber) {
|
|
191043
191205
|
return providedAttemptNumber;
|
|
191206
|
+
}
|
|
191044
191207
|
if (score !== undefined) {
|
|
191045
191208
|
return this.determineAttemptNumber(studentId, lineItemId);
|
|
191046
191209
|
}
|
|
@@ -191064,106 +191227,13 @@ class ProgressRecorder {
|
|
|
191064
191227
|
}
|
|
191065
191228
|
return 0;
|
|
191066
191229
|
}
|
|
191067
|
-
async buildCompletionProgress({
|
|
191068
|
-
studentId,
|
|
191069
|
-
courseId,
|
|
191070
|
-
progressData,
|
|
191071
|
-
resourceId
|
|
191072
|
-
}) {
|
|
191073
|
-
if (typeof progressData.masteredUnits !== "number" || progressData.masteredUnits <= 0) {
|
|
191074
|
-
return;
|
|
191075
|
-
}
|
|
191076
|
-
const masterableUnits = await this.resolveMasterableUnits(resourceId);
|
|
191077
|
-
if (!masterableUnits || masterableUnits <= 0) {
|
|
191078
|
-
log32.warn("[ProgressRecorder] No masterableUnits configured for course", {
|
|
191079
|
-
courseId,
|
|
191080
|
-
resourceId
|
|
191081
|
-
});
|
|
191082
|
-
return;
|
|
191083
|
-
}
|
|
191084
|
-
const facts = await this.fetchEnrollmentAnalyticsFacts(studentId, courseId);
|
|
191085
|
-
if (!facts) {
|
|
191086
|
-
log32.warn("[ProgressRecorder] Unable to retrieve analytics for mastery-based completion", { studentId, courseId });
|
|
191087
|
-
return;
|
|
191088
|
-
}
|
|
191089
|
-
const historicalMasteredUnits = this.sumAnalyticsMetric(facts, "masteredUnits");
|
|
191090
|
-
const totalMastered = historicalMasteredUnits + progressData.masteredUnits;
|
|
191091
|
-
const rawPct = totalMastered / masterableUnits * 100;
|
|
191092
|
-
const pctCompleteApp = Math.min(100, Math.max(0, Math.round(rawPct)));
|
|
191093
|
-
const masteryAchieved = totalMastered >= masterableUnits;
|
|
191094
|
-
return { pctCompleteApp, masteryAchieved };
|
|
191095
|
-
}
|
|
191096
|
-
async fetchEnrollmentAnalyticsFacts(studentId, courseId) {
|
|
191097
|
-
try {
|
|
191098
|
-
const enrollments = await this.edubridgeNamespace.enrollments.listByUser(studentId);
|
|
191099
|
-
const enrollment = enrollments.find((e2) => e2.course.id === courseId);
|
|
191100
|
-
if (!enrollment) {
|
|
191101
|
-
log32.warn("[ProgressRecorder] Enrollment not found for student/course", {
|
|
191102
|
-
studentId,
|
|
191103
|
-
courseId
|
|
191104
|
-
});
|
|
191105
|
-
return;
|
|
191106
|
-
}
|
|
191107
|
-
const analytics = await this.edubridgeNamespace.analytics.getEnrollmentFacts(enrollment.id);
|
|
191108
|
-
return analytics.facts;
|
|
191109
|
-
} catch (error2) {
|
|
191110
|
-
log32.error("[ProgressRecorder] Failed to load enrollment analytics facts", {
|
|
191111
|
-
studentId,
|
|
191112
|
-
courseId,
|
|
191113
|
-
error: error2
|
|
191114
|
-
});
|
|
191115
|
-
return;
|
|
191116
|
-
}
|
|
191117
|
-
}
|
|
191118
|
-
sumAnalyticsMetric(facts, metric) {
|
|
191119
|
-
if (!facts) {
|
|
191120
|
-
return 0;
|
|
191121
|
-
}
|
|
191122
|
-
let total = 0;
|
|
191123
|
-
Object.values(facts).forEach((dateFacts) => {
|
|
191124
|
-
Object.values(dateFacts).forEach((subjectFacts) => {
|
|
191125
|
-
const metrics = subjectFacts.activityMetrics;
|
|
191126
|
-
if (metrics && typeof metrics[metric] === "number") {
|
|
191127
|
-
total += metrics[metric];
|
|
191128
|
-
}
|
|
191129
|
-
});
|
|
191130
|
-
});
|
|
191131
|
-
return total;
|
|
191132
|
-
}
|
|
191133
|
-
async resolveMasterableUnits(resourceId) {
|
|
191134
|
-
if (!resourceId) {
|
|
191135
|
-
return;
|
|
191136
|
-
}
|
|
191137
|
-
const cached = this.cacheManager.getResourceMasterableUnits(resourceId);
|
|
191138
|
-
if (cached !== undefined) {
|
|
191139
|
-
return cached === null ? undefined : cached;
|
|
191140
|
-
}
|
|
191141
|
-
try {
|
|
191142
|
-
const resource = await this.onerosterNamespace.resources.get(resourceId);
|
|
191143
|
-
const playcademyMetadata = resource.metadata?.playcademy;
|
|
191144
|
-
if (!playcademyMetadata) {
|
|
191145
|
-
return;
|
|
191146
|
-
}
|
|
191147
|
-
const masterableUnits = isPlaycademyResourceMetadata(playcademyMetadata) ? playcademyMetadata.mastery?.masterableUnits : undefined;
|
|
191148
|
-
this.cacheManager.setResourceMasterableUnits(resourceId, masterableUnits ?? null);
|
|
191149
|
-
return masterableUnits;
|
|
191150
|
-
} catch (error2) {
|
|
191151
|
-
log32.error("[ProgressRecorder] Failed to fetch resource metadata for mastery config", {
|
|
191152
|
-
resourceId,
|
|
191153
|
-
error: error2
|
|
191154
|
-
});
|
|
191155
|
-
this.cacheManager.setResourceMasterableUnits(resourceId, null);
|
|
191156
|
-
return;
|
|
191157
|
-
}
|
|
191158
|
-
}
|
|
191159
191230
|
async getOrCreateLineItem(lineItemId, activityName, classId, ids) {
|
|
191160
191231
|
try {
|
|
191161
191232
|
const lineItem = await this.onerosterNamespace.assessmentLineItems.findOrCreate(lineItemId, {
|
|
191162
191233
|
sourcedId: lineItemId,
|
|
191163
191234
|
title: activityName,
|
|
191164
191235
|
status: ONEROSTER_STATUS.active,
|
|
191165
|
-
...classId ? { class: { sourcedId: classId } } : { course: { sourcedId: ids.course } }
|
|
191166
|
-
...ids.componentResource ? { componentResource: { sourcedId: ids.componentResource } } : ids.component ? { component: { sourcedId: ids.component } } : {}
|
|
191236
|
+
...classId ? { class: { sourcedId: classId } } : { course: { sourcedId: ids.course } }
|
|
191167
191237
|
});
|
|
191168
191238
|
if (!lineItem.sourcedId) {
|
|
191169
191239
|
throw new TimebackError(`Assessment line item created but has no sourcedId. This should not happen and indicates an upstream API issue.`);
|
|
@@ -191181,21 +191251,15 @@ class ProgressRecorder {
|
|
|
191181
191251
|
}
|
|
191182
191252
|
}
|
|
191183
191253
|
async determineAttemptNumber(studentId, lineItemId) {
|
|
191184
|
-
const
|
|
191185
|
-
if (
|
|
191186
|
-
|
|
191187
|
-
const newAttemptNumber = previousAttemptNumber + 1;
|
|
191188
|
-
log32.debug("[ProgressRecorder] Found previous attempt, incrementing", {
|
|
191189
|
-
previousAttemptNumber,
|
|
191190
|
-
newAttemptNumber,
|
|
191191
|
-
previousScore: latestAttempt.score
|
|
191192
|
-
});
|
|
191193
|
-
return newAttemptNumber;
|
|
191254
|
+
const stats = await this.onerosterNamespace.assessmentResults.getAttemptStats(studentId, lineItemId);
|
|
191255
|
+
if (stats) {
|
|
191256
|
+
return stats.activeAttemptCount + 1;
|
|
191194
191257
|
}
|
|
191195
191258
|
return 1;
|
|
191196
191259
|
}
|
|
191197
191260
|
async createGradebookEntry(lineItemId, studentId, attemptNumber, score, totalQuestions, correctQuestions, xp, masteredUnits, scoreStatus, inProgress, appName) {
|
|
191198
|
-
const
|
|
191261
|
+
const timestamp4 = Date.now().toString(36);
|
|
191262
|
+
const resultId = `${lineItemId}:${studentId}:${timestamp4}`;
|
|
191199
191263
|
await this.onerosterNamespace.assessmentResults.upsert(resultId, {
|
|
191200
191264
|
sourcedId: resultId,
|
|
191201
191265
|
status: ONEROSTER_STATUS.active,
|
|
@@ -195351,7 +195415,8 @@ class TimebackClient {
|
|
|
195351
195415
|
this.edubridge = createEduBridgeNamespace(this);
|
|
195352
195416
|
this.cacheManager = new TimebackCacheManager;
|
|
195353
195417
|
this.studentResolver = new StudentResolver(this.cacheManager, this.oneroster);
|
|
195354
|
-
|
|
195418
|
+
const masteryTracker = new MasteryTracker(this.cacheManager, this.oneroster, this.edubridge);
|
|
195419
|
+
this.progressRecorder = new ProgressRecorder(this.studentResolver, this.cacheManager, this.oneroster, this.caliper, masteryTracker);
|
|
195355
195420
|
this.sessionRecorder = new SessionRecorder(this.studentResolver, this.caliper);
|
|
195356
195421
|
if (this.credentials) {
|
|
195357
195422
|
this._ensureAuthenticated().catch((error2) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playcademy/vite-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.36",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"archiver": "^7.0.1",
|
|
23
23
|
"picocolors": "^1.1.1",
|
|
24
|
-
"playcademy": "0.14.
|
|
24
|
+
"playcademy": "0.14.23"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@inquirer/prompts": "^7.8.6",
|