Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 8x 8x 8x 8x 8x 8x 8x 8x 8x 1x 1x 1x 7x 7x 7x 7x 7x 7x 7x 7x 7x 7x 8x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 8x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 8x 1x 1x 1x 1x 1x 1x 2x 2x 2x 2x 2x 2x 2x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 2x 2x | import { randomUUID } from 'crypto'
import { and, eq } from 'drizzle-orm'
import { db } from '@/db'
import { worksheetMastery } from '@/db/schema'
import { SINGLE_CARRY_PATH } from '@/app/create/worksheets/progressionPath'
import type { GradingResult } from '@/lib/ai/gradeWorksheet'
/**
* Update user's mastery profile based on worksheet grading results
*
* This function:
* 1. Finds or creates a mastery record for the suggested step
* 2. Updates statistics (attempts, accuracy)
* 3. Marks as mastered if threshold is met
* 4. Returns whether mastery was achieved
*/
export async function updateMasteryFromGrading(
userId: string,
gradingResult: GradingResult
): Promise<{ mastered: boolean; stepId: string }> {
const { suggestedStepId, accuracy, totalProblems, correctCount } = gradingResult
// Find the step configuration
const step = SINGLE_CARRY_PATH.find((s) => s.id === suggestedStepId)
if (!step) {
console.warn(`Step ${suggestedStepId} not found in progression path`)
return { mastered: false, stepId: suggestedStepId }
}
const now = new Date()
// Look up existing mastery record
const [existing] = await db
.select()
.from(worksheetMastery)
.where(and(eq(worksheetMastery.userId, userId), eq(worksheetMastery.skillId, suggestedStepId)))
.limit(1)
if (existing) {
// Update existing record
const newTotalAttempts = existing.totalAttempts + totalProblems
const newCorrectAttempts = existing.correctAttempts + correctCount
const overallAccuracy = newCorrectAttempts / newTotalAttempts
// Check if mastery threshold is met
const meetsThreshold = overallAccuracy >= step.masteryThreshold
const meetsMinimum = newTotalAttempts >= step.minimumAttempts
const isMastered = meetsThreshold && meetsMinimum
await db
.update(worksheetMastery)
.set({
totalAttempts: newTotalAttempts,
correctAttempts: newCorrectAttempts,
lastAccuracy: accuracy,
lastPracticedAt: now,
isMastered,
masteredAt: isMastered && !existing.isMastered ? now : existing.masteredAt,
updatedAt: now,
})
.where(eq(worksheetMastery.id, existing.id))
return { mastered: isMastered, stepId: suggestedStepId }
} else {
// Create new record
const overallAccuracy = correctCount / totalProblems
const meetsThreshold = overallAccuracy >= step.masteryThreshold
const meetsMinimum = totalProblems >= step.minimumAttempts
const isMastered = meetsThreshold && meetsMinimum
await db.insert(worksheetMastery).values({
id: randomUUID(),
userId,
skillId: suggestedStepId,
totalAttempts: totalProblems,
correctAttempts: correctCount,
lastAccuracy: accuracy,
firstAttemptAt: now,
lastPracticedAt: now,
isMastered,
masteredAt: isMastered ? now : null,
createdAt: now,
updatedAt: now,
})
return { mastered: isMastered, stepId: suggestedStepId }
}
}
/**
* Get user's mastery progress for all steps in the progression path
*
* Returns an array of step mastery status, useful for showing progression UI
*/
export async function getMasteryProgress(userId: string) {
const masteryRecords = await db
.select()
.from(worksheetMastery)
.where(eq(worksheetMastery.userId, userId))
return SINGLE_CARRY_PATH.map((step) => {
const record = masteryRecords.find((r) => r.skillId === step.id)
return {
stepId: step.id,
stepNumber: step.stepNumber,
name: step.name,
isMastered: record?.isMastered || false,
totalAttempts: record?.totalAttempts || 0,
correctAttempts: record?.correctAttempts || 0,
lastAccuracy: record?.lastAccuracy || null,
masteredAt: record?.masteredAt || null,
lastPracticedAt: record?.lastPracticedAt || null,
}
})
}
|